I am trying to get stock quotes from Yahoo using Swift 3. Although there are some decent tutorials on Swift 2, none of them seem to translate well to Swift 3.
The issue I have at the moment is that in the code below, the session.dataTask is never called. The print statement never fires and the rest of the code that is in there does not work.
I have checked that the request variable looks good, and the url has been tested on the yahoo developer site.
So I'm thinking I must have the syntax of the dataTask wrong or has an error so is skipping completely.
Any thoughts?
urlString = "http://query.yahooapis.com/v1/public/yql?q=select * from yahoo.finance.quotes where symbol IN ('APL')"
//let urlNSS : NSString = urlString as NSString
let urlStr : String = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let url : URL = URL(string: urlStr as String)!
let request = URLRequest(url: url)
let session = URLSession.shared
let config = URLSessionConfiguration.default
let task = session.dataTask(with: request, completionHandler: {(data, response, error) -> Void in
print("in the task")
..
..
)}
task.resume()
If I inspect task I see the following
You must resume the task (task.resume()) that you have created. By default, a task is in suspended state.
I have created a Playground file with a different Yahoo RSS Feed URL. https://drive.google.com/file/d/0B5nqEBSJjCriWl9UTWcxSE42Yk0/view?usp=sharing
The URL you have in your question is not giving any data.
<error xmlns:yahoo="http://www.yahooapis.com/v1/base.rng" yahoo:lang="en-US">
<description>No definition found for Table yahoo.finance.quotes</description>
</error>
Code as Below:
//: Playground - noun: a place where people can play
import UIKit
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
var str = "Hello, playground"
let yahooURLString = "https://feeds.finance.yahoo.com/rss/2.0/headline?s=yhoo®ion=US&lang=en-US"
let yahooRSSURL: URL = URL(string: yahooURLString)!
var request = URLRequest(url: yahooRSSURL)
request.httpMethod = "GET"
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let task = session.dataTask(with: request) {data, response, err in
print("Entered the completionHandler")
print("Response JSON:\n\(String(data: data!, encoding: String.Encoding.utf8)!)")
}
task.resume()
Hope this helps.
Edit: Attaching a screenshot of print statements appearing in the console of playgorund.
Related
I am currently playing around with the Vimeo API and following the setup process and the guided readme found here:
(https://github.com/vimeo/VimeoNetworking)
All I am doing is pulling down publicly available videos from Vimeo except I have been receiving
Fatal error: Session manager did not return a task: file
everything else works and I am able to use my own token for authentication.
Here is the code I have right now that throws this error:
What am I doing wrong or missing?
let queryURL = URL(string: "/channels/staffpicks/videos")
let videoRequest = Request<[VIMVideo]>(path: queryURL!.absoluteString)
guard let sessionClient = _client else {
return []
}
let _ = sessionClient.request(videoRequest, completion: {
results in
switch results {
case .success(let response):
let videos: [VIMVideo] = response.model
for video in videos
{
print("retrieved video: \(video)")
}
vVideo = videos
break
case .failure(let error):
print(error.localizedDescription)
break
}
})
Sorry I am late but this worked for me (using Swift 4.2):
let appConfiguration = AppConfiguration(
clientIdentifier: Constants.VIMEO_CLIENT_IDENTIFIER,
clientSecret: Constants.VIMEO_CLIENT_SECRET,
scopes: [.Public], keychainService: "")
let vimeoSessionManager = VimeoSessionManager.defaultSessionManager(
baseUrl: VimeoBaseURL,
accessToken: Constants.VIMEO_ACCESS_TOKEN,
apiVersion: "3.4")
let vimeoClient = VimeoClient(
appConfiguration: appConfiguration,
sessionManager: vimeoSessionManager)
let videoRequest = Request<[VIMVideo]>(path: "/videos?query=dragon+ball")
vimeoClient.request(videoRequest) {
result in
switch result {
case .success(let response):
let videos: [VIMVideo] = response.model
print("\n\n retrieved videos: \(videos) \n\n")
case .failure(let error):
print("\n\n error retrieving videos: \(error) \n\n")
}
}
I'am almost sure you needed to add a session manager, but not 100% because haven't seen how you initialize the client and the other variables, so I am just adding this example.
Remember to get clientIndentifier, clientSecret and accessToken in [https://developer.vimeo.com/apps][1] (after you've created your app).
This is using a public accessToken, if you need authenticated access just add the .Private and .Interact scopes to the scopes array in appConfiguration, and to get an 'Authenticated' accessToken.
Also, please notice that I am using "/videos?query=dragon+ball" as an example.
I am struggling to set a cookie using Swift 3 into a WKWebView. I could not find any example using Swift 3 so using Swift - How to set cookie in NSMutableURLRequest as a starting point, this is what I have:
let url = URL(string: "https://s3-us-west-2.amazonaws.com/foo/helloworld.html")
/* Create cookie and place in cookie storage */
let cookieStorage = HTTPCookieStorage.shared
let cookieHeaderField = ["Set-Cookie": "somecookie=" + cookieString + ";"]
let cookie = HTTPCookie.cookies(withResponseHeaderFields: cookieHeaderField, for: url!)
cookieStorage.setCookies(cookie, for: url, mainDocumentURL: nil)
let urlRequest = URLRequest.init(url: url!)
theWebView.load(urlRequest)
However, when I use the Simulator and inspect it using Safari Develop, it states that I have no cookies set. Thoughts on what I screwed up on or that I failed to take into account?
Swift 3.0
Use the following line to set cookie for urlRequest:
urlRequest?.setValue("somecookie" + cookieString, forHTTPHeaderField: "Cookie")
I am running a school project and I am new from swift3.
By searching, I know how to pass a data from one view to anther:
Passing data from a tableview to webview
In the post above, he is using http get request to pass data to website, then reload the webivew:
let URL = NSURL(string: "https://www.example.com?data=\(passData)")
webView.loadRequest(NSURLRequest(url: URL! as URL) as URLRequest)
I see some useful links like about code with http post request in here:
HTTP Request in Swift with POST method. In a result, the code can print out the http response.
My question is that, how to implement a webview with sending a http post reuqest, like id, name, etc, instead of get method.
In anther words: I want to reload the webview(like example.com) and that website will contain the value I sent via http post request.
example.com:
$id = $_POST['id'];
echo $id;
Thanks.
Just create a URLRequest for POST as shown in the second link, and pass it to the webView:
var request = URLRequest(url: URL(string: "http://www.example.com/")!)
request.httpMethod = "POST"
let postString = "id=\(idString)"
request.httpBody = postString.data(using: .utf8)
webView.loadRequest(request) //if your `webView` is `UIWebView`
(Consider using WKWebView rather than UIWebView.)
You may need idString to be escaped if it contains some special characters.
By the way, the two line code:
let URL = NSURL(string: "https://www.example.com?data=\(passData)")
webView.loadRequest(NSURLRequest(url: URL! as URL) as URLRequest)
does not seem to be a good Swift 3 code. It can be written as:
let url = URL(string: "https://www.example.com?data=\(passData)")!
webView.loadRequest(URLRequest(url: url))
I know the question has been asked before and I agree with most answers that claim it is better to follow the way requests are made async with URLSession in Swift 3. I haver the following scenario, where async request cannot be used.
With Swift 3 and the ability to run swift on servers I have the following problem.
Server Receives a request from a client
To process the request the server has to send a url request and wait for the response to arrive.
Once response arrives, process it and reply to the client
The problem arrises in step 2, where URLSession gives us the ability to initiate an async data task only. Most (if not all) server side swift web frameworks do not support async responses. When a request arrives to the server everything has to be done in a synchronous matter and at the end send the response.
The only solution I have found so far is using DispatchSemaphore (see example at the end) and I am not sure whether that will work in a scaled environment.
Any help or thoughts would be appreciated.
extension URLSession {
func synchronousDataTaskWithURL(_ url: URL) -> (Data?, URLResponse?, Error?) {
var data: Data?
var response: URLResponse?
var error: Error?
let sem = DispatchSemaphore(value: 0)
let task = self.dataTask(with: url as URL, completionHandler: {
data = $0
response = $1
error = $2 as Error?
sem.signal()
})
task.resume()
let result = sem.wait(timeout: DispatchTime.distantFuture)
switch result {
case .success:
return (data, response, error)
case .timedOut:
let error = URLSessionError(kind: URLSessionError.ErrorKind.timeout)
return (data, response, error)
}
}
}
I only have experience with kitura web framework and this is where i faced the problem. I suppose that similar problems exist in all other swift web frameworks.
In Vapor, you can use the Droplet's client to make synchronous requests.
let res = try drop.client.get("https://httpbin.org")
print(res)
Additionally, you can use the Portal class to make asynchronous tasks synchronous.
let res = try Portal.open { portal in
asyncClient.get("https://httpbin.org") { res in
portal.close(with: res)
}
}
Your three-step problem can be solved via the use of a completion handler, i.e., a callback handler a la Node.js convention:
import Foundation
import Kitura
import HeliumLogger
import LoggerAPI
let session = URLSession(configuration: URLSessionConfiguration.default)
Log.logger = HeliumLogger()
let router = Router()
router.get("/test") { req, res, next in
let datatask = session.dataTask(with: URL(string: "http://www.example.com")!) { data, urlResponse, error in
try! res.send(data: data!).end()
}
datatask.resume()
}
Kitura.addHTTPServer(onPort: 3000, with: router)
Kitura.run()
This is a quick demo of a solution to your problem, and it is by no means following best Swift/Kitura practices. But, with the use of a completion handler, I am able to have my Kitura app make an HTTP call to fetch the resource at http://www.example.com, wait for the response, and then send the result back to my app's client.
Link to the relevant API: https://developer.apple.com/reference/foundation/urlsession/1410330-datatask
I am building both an iOS client and a django backend service. The connection made between the systems is OAUTH2, implemented by the django-oauth2-toolkit.
Although the following command done in curl works (returns an access token):
curl -X POST -d "grant_type=password&username=<user>&password=<password>" http://<clientID>:<clientSecret>#localhost:8000/o/token/
The following Swift snippet, that uses Alamofire, receives "invalid_client", as a response.
let request = "http://\(Authentication.clientId):\(Authentication.clientSecret)#localhost:8000/o/token/"
var URLRequest = NSMutableURLRequest(URL: NSURL(string: request)!)
URLRequest.HTTPMethod = "POST"
let parameters = ["grant_type": "password", "username": in_username.text!, "password": in_password.text!]
let encoding = Alamofire.ParameterEncoding.URL
(URLRequest, _) = encoding.encode(URLRequest, parameters: parameters)
URLRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
Alamofire.request(URLRequest)
.responseJSON { response in
let data = response
print(data)
}
I then traced the InvalidClientError in the django-oauth2-toolkit source, and found that the exception was raised in the highlighted snippet of the following file:
oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py
if self.request_validator.client_authentication_required(request):
log.debug('Authenticating client, %r.', request)
print(request) # I included this print message to inspect the request variable in console.
if not self.request_validator.authenticate_client(request):
log.debug('Client authentication failed, %r.', request)
raise errors.InvalidClientError(request=request) # RAISED!
I included the print(request) line to inspect the differences between the request made by curl and by Alamofire. The major difference was that the curl version included an authorization key:
'Authorization': 'Basic Z3ZFSjVXejloUGgybUJmdDNRaGhXZnlhNHpETG5KY3V6djJldWMwcjpSbVNPMkpwRFQ4bHp1UVFDYXN3T3dvVFkzRTBia01YWWxHVHNMcG5JUGZCUHFjbHJSZE5EOXQzd3RCS2xwR09MNWs1bEE4S2hmRUkydEhvWmx3ZVRKZkFXUDM4OERZa1NTZ0RvS0p3WjUyejRSQ29WRkZBS01RS1lydEpsTWNXag=='
and the Alamofire request didn't.
I highly suspect this is the culprit, but I really don't know else to do though from here on. I would really appreciate any wisdom.
Found the answer!
Was reading through a RFC document on the HTTP protocol, when this section caught my eye.
https://www.rfc-editor.org/rfc/rfc1945#section-11.1
Specifically,
To receive authorization, the client sends the user-ID and password,
separated by a single colon (":") character, within a base64 [5]
encoded string in the credentials.
It seems that Alamofire does not encode in 64 bits the clientId and clientSecret, as expected. Obviously, curl does this automatically. So I did the following:
First encode:
static let clientData: NSData = "\(clientId):\(clientSecret)".dataUsingEncoding(NSUTF8StringEncoding)!
static let client64String = clientData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.init(rawValue: 0))
Then set the request header using the resulting value:
let request = "http://localhost:8000/o/token/"
var URLRequest = NSMutableURLRequest(URL: NSURL(string: request)!)
URLRequest.HTTPMethod = "POST"
let parameters = ["grant_type": "password",
"username": in_username.text!,
"password": in_password.text!,
]
let encoding = Alamofire.ParameterEncoding.URL
(URLRequest, _) = encoding.encode(URLRequest, parameters: parameters)
// SOLUTION!
URLRequest.setValue("Basic \(Authentication.client64String)", forHTTPHeaderField: "Authorization")
Alamofire.request(URLRequest)
.responseJSON { response in
let data = response
print(data)
}
I then received the expected token as a response.