JSONService
During the http request process, completion nil is returned. completion does not work.. but print function is working. how can i fix this problem. I am using http request for the first time. my English is bad. I'm sorry for that.
class JSONService {
let parameters = ["index": "articles"]
func getNews(completion: #escaping (NewsRoot) -> ()) {
guard let url = URL(string: "http://yazar.io/api/v2/search/") else { return }
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
guard error == nil else { return }
guard let data = data else { return }
do {
let news = try JSONDecoder().decode(NewsRoot.self, from: data)
DispatchQueue.main.async {
completion(news) //does not work
print(news) //works
}
} catch let error {
print(error.localizedDescription)
}
}
task.resume()
}
}
DataStore
class DataStore: ObservableObject {
#Published var news: [News] = []
init() {
getNews()
}
func getNews() {
JSONService().getNews { (news) in
self.news = news.articles
}
}
}
ContentView
#ObservedObject var news = DataStore()
Related
https://www.pickleballtournaments.com/oncourts.pl?tid=2027&venue=Main
you can view that source and see a near bottom...
Was able to get html before now there is a google script near the bottom and htmlString is now nil. Anyone can get this html? of course gets html for other sites! so the is the blocking issue here! hope we can get the html again please and thank you. Mike
'/table>
'script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');ga('create', 'UA-82207385-1', 'auto');ga('send', 'pageview');
override func viewDidLoad() {
super.viewDidLoad()
var url = URL(string: "https://www.pickleballtournaments.com/oncourts.pl?tid=2027&venue=Main")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error)
} else {
let htmlContent = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print("28 htmlContent: \(htmlContent)")
}
}
task.resume()
28 htmlContent: nil
works great gets data with that line in the html content!
override func viewDidLoad() {
super.viewDidLoad()
var url = URL(string: "https://www.pickleballtournaments.com/oncourts.pl?tid=2027&venue=Main")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error)
} else {
let htmlContent = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
let data = data?.html2String
print("28 data: \(data)"
}
}
task.resume()
}
I am trying to make a POST request to a Firebase Notifications API using Vapor 1.5 and Firebase Legacy Protocol, but I get failure response.
response is JSON(node: Node.Node.object(["multicast_id":
Node.Node.number(5936281277445399934), "failure": Node.Node.number(0),
"canonical_ids": Node.Node.number(0), "results":
Node.Node.array([Node.Node.object(["message_id":
Node.Node.string("0:1527074314969790%c7ade8b9f9fd7ecd")])]),
"success": Node.Node.number(1)]))
EDIT
Making the request through POSTMan fails with error "The request was missing an Authentication Key (FCM Token)."
class FirebaseRequester {
let fcmLegacyServerKey = "AIzaSyDSuXXXXXXkCafTQay5_r8j3snvVos"
func sendNotification(payLoad: JSON) throws -> Response {
var response: Response?
do {
let responseFCM = try drop.client.post("https://fcm.googleapis.com/fcm/send",
headers: ["Content-Type":"application/json","Authorization": "key\(fcmLegacyServerKey)"],
query: [:],
body: payLoad.makeBody())
response = responseFCM
}catch let error {
let message = error.localizedDescription
logErr.prints(message: message)
throw Abort.custom(status: .badRequest, message: message)
}
guard let rsp = response?.json else {
let message = "no json received on line \(#line)"
drop.log.error(message)
logErr.prints(message: message)
throw Abort.custom(status: .badRequest, message: message)
}
print("rsp in json format is \(rsp)")
return response!
}//end of sendNotification()
}//end of class FirebaseRequester
//make another class here and initialize it with FirebaseRequester
//get data from Client App
// validate data
// finally, create the payLoad and call sendNotification(:)
//request should look like
{
"aps": {
"alert": "Breaking News!",
"sound": "default",
"link_url": "https://raywenderlich.com"
}
}
let fcmKeyToSendTo = "someDeviceTokenKeyReceivedFromClient_biHZNI-e9E53WEkCzrki"
let data = try Node(node: ["alert": "alert", "sound": "sound", "link_url": "https://www.someWebsite.com"])
var payLoadObj = try JSON(node: ["aps" : data])
payLoadObj["to"] = try JSON(node: fcmKeyToSendTo)
do {
let _ = try firebaseRequester.sendNotification(payLoad: payLoadObj)
}catch{
logErr.prints(message: error.localizedDescription)
}
let message = "notification Sent"
return try JSON(node:["success":message])
In sendNotification(payload:) I had a typo, I missed = after key. It should have been "key=\(fcmLegacyServerKey)"
In sendNotification(payload:), payLoad.makeBody should not be called, I should have just passed the JSON object payLoad as an argument to the .post request.
The JSON object of the notification was clearly badly formatted from the outset. The message type I wanted to send was notification, but I was passing in a key named aps. I should have passed key notification as shown below.
.
class FirebaseRequester {
let fcmLegacyServerKey = "AIzaSy....vVos"
func sendNotification(payLoad: JSON) throws -> Response {
var response: Response?
do {
let responseFCM = try drop.client.post("https://fcm.googleapis.com/fcm/send",
headers: ["Content-Type":"application/json","Authorization": "key=\(fcmLegacyServerKey)"],
query: [:],
body: payLoad
response = responseFCM
}catch let error {
let message = error.localizedDescription
logErr.prints(message: message)
throw Abort.custom(status: .badRequest, message: message)
}
guard let rsp = response?.json else {
let message = "no json received on line \(#line)"
drop.log.error(message)
logErr.prints(message: message)
throw Abort.custom(status: .badRequest, message: message)
}
return response!
}//end of sendNotification()
}//end of class FirebaseRequester
class TestRouteNow {
let firebaseRequester: FirebaseRequester
init(firebaseRequester: FirebaseRequester) {
self.firebaseRequester = firebaseRequester
}
func addRoutes(drop: Droplet) {
drop.post("test", "notif", handler: postNotification)
}
func postNotification(request: Request) throws -> ResponseRepresentable {
let fcmDevice = "someDeviceTokenReceivedFromClientApp"
let data = try Node(node: ["title": "title","body": "body", "sound": "default", "badge":"60"])
var payLoadObj = try JSON(node: ["notification": data])
payLoadObj["to"] = try JSON(node: fcmDevice)
do {
let _ = try firebaseRequester.sendNotification(payLoad: payLoadObj)
}catch{
logErr.prints(message: error.localizedDescription)
}
let message = "notification Sent"
return try JSON(node:["success":message])
}
}//end of class
// request body
{
"to" : "cQDtm_someDeviceTokenReceivedFromClient",
"priority":"high",
"notification": {
"title":"Booking Rescheduled",
"body": "Cancelled Booking 7830593, for Mon, 12 March",
"sound":"default",
"badge": "100"
}
}
Working on an app that has two parts - Rider & Driver. When the driver accepts the request, an alert is then sent to the rider that the request was accepted and driver is on the way.
Unable to trigger the alert to the rider.
RiderVC:
func driverAcceptedRequest(requestAccepted: Bool, driverName: String) {
if !riderCancelledRequest {
if requestAccepted {
self.alertTheUser(title: "Ryde Accepted", message: "\(driverName) Has Accepted Your Ryde Request and will message you with details")
} else {
RydeHandler.Instance.cancelRyde()
alertTheUser(title: "Ryde Cancelled", message: "\(driverName) Has Cancelled the Ryde Request")
}
}
riderCancelledRequest = false
}
RydeHandler.swift:
// DRIVER ACCEPTED RYDE
DataService.Instance.requestAcceptedRef.observe(FIRDataEventType.childAdded) { (snapshot: FIRDataSnapshot) in
if let data = snapshot.value as? NSDictionary {
if let name = data[Constants.NAME] as? String {
if self.driver == "" {
self.driver = name
self.delegate?.driverAcceptedRequest(requestAccepted: true, driverName: self.driver)
}
}
}
}
Firebase database structure:
Edit
ViewDidLoad in tableviewcontroller - list of requests:
ref.child("drivers").child("RideRequests").observe(FIRDataEventType.value, with: { snapshot in
self.rideRequests.removeAll()
for item in snapshot.children{
self.rideRequests.append(item as! FIRDataSnapshot)
}
self.rideRequests.reverse()
self.tableView.reloadData()
})
I have this code which- as far as I understand- is async:
func testConnection() -> Bool {
let url = URL(string: uri)!
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
guard let data = data, let _:URLResponse = response , error == nil else {
print("error")
return
}
let dataString = String(data: data, encoding: String.Encoding.utf8)
}
task.resume()
}
How would a synchronous version look that allows to return the testConnection result to the sender?
You could use a semaphore to wait for the async call to finish:
func testConnection() -> Bool {
let url = URL(string: uri)!
var dataStringOrNil: String?
let semaphore = DispatchSemaphore(value: 0)
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
defer {
semaphore.signal()
}
guard let data = data, error == nil else {
print("error")
return
}
dataStringOrNil = String(data: data, encoding: .utf8)
}
task.resume()
semaphore.wait()
guard let dataString = dataStringOrNil else {
return false
}
// some substring checking
return dataString.contains("href")
}
I wouldn't recommend it, but there can be reasons for sync calls. We use them sometimes in command-line tools.
I'm new here, and this is my first question...
I try to write an App in Swift 2 that makes an HTTP POST request but I can't figure out how to use the new error handling of Swift 2. Can anyone tell me please how to implement the "do-try-catch" error handling of Swift 2 to the code snippet below?
(This code snippet uses the old error handling of swift 1.2)
func post(params : Dictionary<String, String>, url : String) {
var request = NSMutableURLRequest(URL: NSURL(string: url)!)
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil/*, error: &err*/)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
print("Response: \(response)")
var strData = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Body: \(strData)")
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves/*, error: &err*/) as? NSDictionary
// Did the JSONObjectWithData constructor return an error? If so, log the error to the console
if(err != nil) {
print(err!.localizedDescription)
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Error could not parse JSON: '\(jsonStr)'")
}
else {
// The JSONObjectWithData constructor didn't return an error. But, we should still
// check and make sure that json has a value using optional binding.
if let parseJSON = json {
// Okay, the parsedJSON is here, let's get the value for 'success' out of it
var success = parseJSON["success"] as? Int
print("Succes: \(success)")
}
else {
// Woa, okay the json object was nil, something went worng. Maybe the server isn't running?
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Error could not parse JSON: \(jsonStr)")
}
}
})
task!.resume()
}
You presumably want to wrap your NSJSONSerialization calls in do/try/catch logic as shown below.
In Swift 3:
var request = URLRequest(url: URL(string: urlString)!)
let session = URLSession.shared
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.httpBody = try! JSONSerialization.data(withJSONObject: parameters)
// or if you think the conversion might actually fail (which is unlikely if you built `parameters` yourself)
//
// do {
// request.httpBody = try JSONSerialization.data(withJSONObject: parameters)
// } catch {
// print(error)
// }
let task = session.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print("error: \(error)")
return
}
// this, on the other hand, can quite easily fail if there's a server error, so you definitely
// want to wrap this in `do`-`try`-`catch`:
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] {
let success = json["success"] as? Int // Okay, the `json` is here, let's get the value for 'success' out of it
print("Success: \(success)")
} else {
let jsonStr = String(data: data, encoding: .utf8) // No error thrown, but not dictionary
print("Error could not parse JSON: \(jsonStr)")
}
} catch let parseError {
print(parseError) // Log the error thrown by `JSONObjectWithData`
let jsonStr = String(data: data, encoding: .utf8)
print("Error could not parse JSON: '\(jsonStr)'")
}
}
task.resume()
Or, in Swift 2
let request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(parameters, options: [])
// or if you think the conversion might actually fail (which is unlikely if you built `parameters` yourself)
//
// do {
// request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: [])
// } catch {
// print(error)
// }
let task = session.dataTaskWithRequest(request) { data, response, error in
guard let data = data where error == nil else {
print("error: \(error)")
return
}
// this, on the other hand, can quite easily fail if there's a server error, so you definitely
// want to wrap this in `do`-`try`-`catch`:
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary {
let success = json["success"] as? Int // Okay, the `json` is here, let's get the value for 'success' out of it
print("Success: \(success)")
} else {
let jsonStr = String(data: data, encoding: NSUTF8StringEncoding) // No error thrown, but not NSDictionary
print("Error could not parse JSON: \(jsonStr)")
}
} catch let parseError {
print(parseError) // Log the error thrown by `JSONObjectWithData`
let jsonStr = String(data: data, encoding: NSUTF8StringEncoding)
print("Error could not parse JSON: '\(jsonStr)'")
}
}
task.resume()
I'd also suggest being a little more careful about forced unwrapping of data, because you want to detect/handle the errors, not crash. For example, above I use a guard statement to unwrap it.
In general if a function throws you have to write it inside a do catch block or just mark the outer scope function (in this case post) as throws:
func post(params : Dictionary<String, String>, url : String) {
let request = NSMutableURLRequest(URL: NSURL(string: url)!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: .PrettyPrinted)
} catch {
//handle error. Probably return or mark function as throws
print(error)
return
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
// handle error
guard error == nil else { return }
print("Response: \(response)")
let strData = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Body: \(strData)")
let json: NSDictionary?
do {
json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary
} catch let dataError {
// Did the JSONObjectWithData constructor return an error? If so, log the error to the console
print(dataError)
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Error could not parse JSON: '\(jsonStr)'")
// return or throw?
return
}
// The JSONObjectWithData constructor didn't return an error. But, we should still
// check and make sure that json has a value using optional binding.
if let parseJSON = json {
// Okay, the parsedJSON is here, let's get the value for 'success' out of it
let success = parseJSON["success"] as? Int
print("Succes: \(success)")
}
else {
// Woa, okay the json object was nil, something went worng. Maybe the server isn't running?
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Error could not parse JSON: \(jsonStr)")
}
})
task!.resume()
}
If you don't want to handle these errors right in your post function you can just declare it as throws than you don't have to use do catch at all