I am new to swift and ios programming, I have the following block of code which queries the url for some songs and I should get the songs back.
let defaultSession = URLSession(configuration: URLSessionConfiguration.default)
var dataTask: URLSessionDataTask?
if dataTask != nil {
dataTask?.cancel()
}
let expectedCharSet = CharacterSet.urlQueryAllowed
let searchTerm = term.addingPercentEncoding(withAllowedCharacters: expectedCharSet)!
let url = URL(string: "https://itunes.apple.com/search?media=music&entity=song&term=\(term)")
print("URL: ", url)
// building up a dataTask computation
dataTask = defaultSession.dataTask( with: url!)
{(maybe_data, response, error) in
if error != nil {
print("Error: ", error!.localizedDescription)
// closure(nil)
return
} else if let httpResponse = response as? HTTPURLResponse {
print("no error")
if httpResponse.statusCode == 200 {
if let data = maybe_data {
// closure(songData)
print("songData: ", data)
} else {
print("no song data")
return
}
}
}
}
// does htis run the data task? is it a promise that's build?
dataTask?.resume()
The problem is that I want to get the data out of the completion handler, so ideally I'd write
let music = dataTask?.resume()
However, I am not sure how to output the value.
Related
Hi i am a beginner studying swift and would like to know what to use when making an api request. What is the modern and professional way?
is it using an escaping closure like so:
func getTrendingMovies(completion: #escaping (Result< [Movie], Error >) -> Void) {
guard let url = URL(string: "\(Constants.baseUrl)/trending/all/day?api_key=\.(Constants.API_KEY)") else {return}
let task = URLSession.shared.dataTask(with: URLRequest(url: url)) { data, _,
error in
guard let data = data, error == nil else {
return
}
do {
let results = try JSONDecoder().decode(TrendingMoviesResponse.self, from:
data)
completion(.success(results.results))
} catch {
completion(.failure(error))
}
}
task.resume()
}
or should i make an api request without escaping closure while using a sort of delegate like so:
func performRequest(with urlString: String){
if let url = URL(string: urlString){
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
delegate?.didFailWithError(error: error!)
return
}
if let safeData = data{
// created parseJson func
if let weather = parseJSON(safeData){
delegate?.didUpdateWeather(self,weather: weather)
}
}
}
task.resume()
} else {
print("url is nil")
}
}
I agree with matt, the modern and professional way is async/await
func getTrendingMovies() async throws -> [Movie] {
let url = URL(string: "\(Constants.baseUrl)/trending/all/day?api_key=\(Constants.API_KEY)")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(TrendingMoviesResponse.self, from: data).results
}
Please check if there's something wrong
I have a block of code that use API to get a list of film but nothing happens.
typealias JSONDictHandler = (([String : Any]?) -> Void)
let session = URLSession(configuration: .default)
var request = URLRequest(url: URL(string: "http://dev.bsp.vn:8081/training-movie/movie/list")!)
let token = "dCuW7UQMbdvpcBDfzolAOSGFIcAec11a"
request.httpMethod = "GET"
request.setValue(token, forHTTPHeaderField: "app_token")
let dataTask = session.dataTask(with: request) { (data, response, error) in
// if nothing wrong
if error == nil {
if let httpResponse = response as? HTTPURLResponse {
switch httpResponse.statusCode {
case 200: // successful case
if let data = data {
do {
let jsonDict = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
print(jsonDict)
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
}
}
default:
print("HTTP Response Code: \(httpResponse.statusCode)")
}
}
// there's something wrong
} else {
print("Error: \(String(describing: error?.localizedDescription))")
}
}
dataTask.resume()
Besides, there're some parameters I have to insert to the url, how can I do this?
Hope this is helpful!!
Allow arbitrary load to yes
Can anyone help me with this API code. I got everything but one error fixed.
Here is my code:
let APIUrl = NSURL(string:"https://api.openweathermap.org/data/2.5/weather? lat=35&lon=150&appid=e7b2054dc37b1f464d912c00dd309595&units=Metric")
var request = URLRequest(url:APIUrl! as URL)
let task = URLSession.shared.dataTask(with: request as URLRequest)
guard let data = Data else {return}
let decoder = JSONDecoder()
let weatherData = try decoder.decode(MyWeather, from: data)
let ggtemp = weatherData.main?.temp
print(ggtemp, "THIS IS THE TEMP")
DispatchQueue.main.async {
tempDisplay.text = String (ggtemp) + " c"
}
}
Image of error
Once I fix the "let data = data" error, I get an error on the "let task = URLSesss..."
Any help would be appreciated. Thanks in advance.
Try this code
let APIUrl = NSURL(string:"https://api.openweathermap.org/data/2.5/weather?lat=35&lon=150&appid=e7b2054dc37b1f464d912c00dd309595&units=Metric")
var request = URLRequest(url:APIUrl! as URL)
request.httpMethod = "GET"
let dataTask = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error ?? "Error is empty.")
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse ?? "HTTP response is empty.")
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
do {
let weatherData = try JSONDecoder().decode(MyWeather.self, from: responseData)
let ggtemp = weatherData.main?.temp
print(ggtemp, "THIS IS THE TEMP")
DispatchQueue.main.async {
tempDisplay.text = String (ggtemp) + " c"
}
} catch {
print("error parsing response from POST on /todos")
return
}
})
dataTask.resume()
I have made a simple app which adds data to a database, and then retrieves it. Whilst creating unit testing, it appears that the URLSession.Shared.dataTask is not running. I can see this through the output of the print statements I have setup. Below is my code:
func addChild(childName:String,dob:String,number1:String,number2:String,parentNum:String,parentPass:String,notes:String){
//url to php file
let url = NSURL(string:"http://localhost/addChild.php")
//request to this file
let request = NSMutableURLRequest(url: url as! URL)
//method to pass data to this file
request.httpMethod = "POST"
//body to be appended to url
let body = "childName=\(childName)&dateOfBirth=\(dob)&contact1=\(number1)&contact2=\(number2)&parentAccNum=\(parentNum)&parentAccPass=\(parentPass)¬es=\(notes)"
request.httpBody = body.data(using: String.Encoding.utf8)
print("a")
//launching the request
URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { (data:Data?, response:URLResponse?, error:Error?) in
print("b")
if (error == nil){
print("c")
//send request
//get main queue in code process to communicate back to user interface
DispatchQueue.main.async(execute: {
do{
//get json result
let json = try JSONSerialization.jsonObject(with: data!,options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
print("d")
//assigning json to parseJSON in guard/secure way
//checking whether the parsing has worked
guard let parseJSON = json else{
print("Error while parsing")
return
}
//get id from parseJSON dictionary
let id = parseJSON["id"]
//if there is some id value
if id != nil{
print(parseJSON)
self.success = true
print("success")
}
}
catch{
print("Caught an error:\(error)")
}
} )
}
//if unable to proceed request
else{
print("Error:\(error)")
}
//launch prepared session
}).resume()
}
And then below is my unit testing script:
import XCTest
#testable import computerScienceCoursework
class addChildTest: XCTestCase {
//Setting up the values of the text fields
var testChildName:String = "Test name"
var testDOB:String = "99/99/99"
var testContact1:String = "00000000000"
var testContact2:String = "11111111111"
var testParAccNum:String = "-1"
var testParAccPass:String = "Password"
var testNotes:String = "Insert notes here"
var newChild = AddChildController()
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testAddChildIsWorking(){
//Assigning the values to the text fields
newChild.addChild(childName: testChildName,dob: testDOB,number1: testContact1,number2: testContact2,parentNum: testParAccNum,parentPass: testParAccPass,notes: testNotes)
XCTAssert(newChild.success == true)
}
}
Problem here is that you donĀ“t know when the async task is finished and the success property is getting updated.
There are some possible solutions for your problem one of them is to add a completion handler to your method.
func addChild(childName:String,dob:String,number1:String,number2:String,parentNum:String,parentPass:String,notes:String, completion: (Bool) -> Void){
//url to php file
let url = NSURL(string:"http://localhost/addChild.php")
//request to this file
let request = NSMutableURLRequest(url: url as! URL)
//method to pass data to this file
request.httpMethod = "POST"
//body to be appended to url
let body = "childName=\(childName)&dateOfBirth=\(dob)&contact1=\(number1)&contact2=\(number2)&parentAccNum=\(parentNum)&parentAccPass=\(parentPass)¬es=\(notes)"
request.httpBody = body.data(using: String.Encoding.utf8)
print("a")
//launching the request
URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { (data:Data?, response:URLResponse?, error:Error?) in
print("b")
if (error == nil){
print("c")
//send request
//get main queue in code process to communicate back to user interface
DispatchQueue.main.async(execute: {
do{
//get json result
let json = try JSONSerialization.jsonObject(with: data!,options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
print("d")
//assigning json to parseJSON in guard/secure way
//checking whether the parsing has worked
guard let parseJSON = json else{
print("Error while parsing")
completion(false)
return
}
//get id from parseJSON dictionary
let id = parseJSON["id"]
//if there is some id value
if id != nil{
print(parseJSON)
self.success = true
print("success")
completion(true)
}
}
catch{
print("Caught an error:\(error)")
completion(false)
}
} )
}
//if unable to proceed request
else{
print("Error:\(error)")
completion(false)
}
//launch prepared session
}).resume()
}
Then in your test method you can the method.
func testAddChildIsWorking()
{
let asyncExpectation = expectationWithDescription("addChildIsWorkingFunction")
newChild.addChild(childName: testChildName, dob: testDOB, number1: testContact1,
number2: testContact2, parentNum: testParAccNum, parentPass: testParAccPass, notes: testNotes) { (success) in
asyncExpectation.fulfill()
}
self.waitForExpectationsWithTimeout(10) { error in
XCTAssert(newChild.success == true)
}
}
waitForExpectationWithTimeout is waiting until a fulfill is trigger or a timeout occurs. In this way you could test your async code.
For more informations check this link
Hope that helps.
I am following a tutorial on accessing an api and parsing the result. I am following the tutorial word for word but I cannot run the program because of 'Missing return in a function expected to return 'NSURLSessionDataTask'
so I changed the return statement to "return NSURLSessionDataTask" but then got an error saying "Cannot convert return expression of type 'NSURLSessionDataTask.Type" to return type 'NSURLSessionDataTask'
How do i figure out the return type? do I even need a return? because in the tutorial there is not return statement (i tried without return as well).
func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void)
-> NSURLSessionDataTask {
let postEndpoint: String = "http://jsonplaceholder.typicode.com/posts/1"
let urlRequest = NSURLRequest(URL: NSURL(string: postEndpoint)!)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlRequest, completionHandler: {
(data, response, error) in
guard let responseData = data else {
print("Error: did not recieve data")
return
}
guard error == nil else {
print("error calling GET on /posts/1")
print(error)
return
}
// parse the resutl as JSON, since that's what the API provieds
let post: NSDictionary
do {
post = try NSJSONSerialization.JSONObjectWithData(responseData, options: []) as! NSDictionary
} catch {
print("error trying to convert data to JSON")
return
}
// now we have the post, let's just print it to prove we can access it
print("The post is: " + post.description)
if let postTitle = post["title"] as? String {
print("The title is: " + postTitle)
}
})
// and send it
task.resume()
}
Did you really mean to write your own method called dataTaskWithRequest which looks just like the NSURLSession method of the same name? The problem is that you said you're writing a method that returns a NSURLSessionTask object, but you don't return anything.
I'd think you meant something like the following, renaming your own method to something else, and specifying that it's not returning anything itself, because it's not:
func performRequest() {
let postEndpoint: String = "http://jsonplaceholder.typicode.com/posts/1"
let urlRequest = NSURLRequest(URL: NSURL(string: postEndpoint)!)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlRequest, completionHandler: {
(data, response, error) in
guard let responseData = data else {
print("Error: did not recieve data")
return
}
guard error == nil else {
print("error calling GET on /posts/1")
print(error)
return
}
// parse the resutl as JSON, since that's what the API provieds
let post: NSDictionary
do {
post = try NSJSONSerialization.JSONObjectWithData(responseData, options: []) as! NSDictionary
} catch {
print("error trying to convert data to JSON")
return
}
// now we have the post, let's just print it to prove we can access it
print("The post is: " + post.description)
if let postTitle = post["title"] as? String {
print("The title is: " + postTitle)
}
})
// and send it
task.resume()
}