swift uploadTaskWithRequest with didReceiveData - swift

I am pretty new to swift i have the following code
var data : AnyObject
let dict = jsonObject as NSDictionary
do
{
data = try NSJSONSerialization.dataWithJSONObject(dict, options:.PrettyPrinted)
let strData = NSString(data: data as! NSData, encoding: NSUTF8StringEncoding)! as String
data = strData.dataUsingEncoding(NSUTF8StringEncoding)!
let task = defaultSession.uploadTaskWithRequest(request, fromData: data as? NSData,
completionHandler:
{(data,response,error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil
else {
return
}
});
task.resume()
}catch{
return resultJson
}
the resultJson object returns a empty array as there are more date to be downloaded and it takes time.I am wondering weather i can use didReceiveData option to return data after it is downloaded. I searched for code online but couldn't find any. Any help with code is much appreciated.
Thanks

You're right in that the return will run before the upload is finished, and therefore you won't get the desired result. I assume this code is in a function. You need to change it to take a closure as a parameter. Then when the upload is finished, you call that closure. Something like:
func doTheUpload(completion completionHandler: ((AnyObject?) -> Void)) {
var data : AnyObject
let dict = jsonObject as NSDictionary
do
{
data = try NSJSONSerialization.dataWithJSONObject(dict, options:.PrettyPrinted)
let strData = NSString(data: data as! NSData, encoding: NSUTF8StringEncoding)! as String
data = strData.dataUsingEncoding(NSUTF8StringEncoding)!
let task = defaultSession.uploadTaskWithRequest(request, fromData: data as? NSData,
completionHandler:
{(data,response,error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil
completionhandler(resultJson)
else {
return
}
});
task.resume()
}catch{
// Do something for error
}
}
Then you would call it as:
doTheUpload(completion: {
resultJson in
// use the result
})
Note that I don't know where you're getting resultJson from or what type it is, so you will have to make some changes.

Related

Use of unresolved identifier 'self' in Swift 5

I wanted to write a weather app using OpenWeatherMap Api. Everything looks fine for me. The app was written using an tutorial on the web and everything looks 1:1.
let weatherURL = URL(string: "http://api.openweathermap.org/data/2.5/weather?q=Warsaw,pl?&units=imperial&APPID=cb9325925ed023b38a64c3d8da3c345c")!
let dataTask = session.dataTask(with: weatherURL) {
(data: Data?, response: URLResponse?, error: Error?) in
if let error = error {
print("Error:\n\(error)")
} else {
if let data = data {
let dataString = String(data: data, encoding: String.Encoding.utf8)
print("All the weather data:\n\(dataString!)")
if let jsonObj = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary {
if let mainDictionary = jsonObj.value(forKey: "main") as? NSDictionary {
if let temperature = mainDictionary.value(forKey: "temp") {
DispatchQueue.main.async {
self.weatherLabel.text = "Temperatura w Warszawie: \(temperature)°C"
}
}
} else {
print("Error: unable to find temperature in dictionary")
}
} else {
print("Error: unable to convert json data")
}
} else {
print("Error: did not receive data")
}
}
}```
The problem is that your code
let weatherURL = ...
let dataTask = ...
...needs to be inside some method of some struct or class. If you look at the original tutorial, you'll see that that's the case. Your code is just hanging out loose in the open, in a place where executable code is not permitted.

Array is null after setting data in it

I have a JSON request that gets data from the Darksky API, I get the data properly and it is showing on the screen. However, When i'm trying to set the data from the array I get from the JSON call in another array, it stays empty.
This is my code:
just declaring the array:
var mForecastArray = [Weather]()
this is the function that calls the API:
func getForecast(){
Weather.forecast(withLocation: "37.8267,-122.4233") { (arr) in
DispatchQueue.main.async {
self.mForecastArray = arr
self.mTodayWeather = arr[0]
self.mCollectionView.reloadData()
}
}
}
The weird part is that it does work, and the data do shows on screen, but still, mForecastArray seems null.
This is the API call itself:
static func forecast(withLocation location: String, completion: #escaping ([Weather]) -> ()){
let url = basePath + location
let request = URLRequest(url: URL(string: url)!)
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
var forecastArray: [Weather] = []
if let data = data{
do{
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any]{
if let dailyForecast = json["daily"] as? [String:Any]{
if let dailyData = dailyForecast["data"] as? [[String:Any]]{
for dataPoint in dailyData{
if let weatherObject = try? Weather(json: dataPoint){
forecastArray.append(weatherObject)
}
}
}
}
}
}catch{
print(error.localizedDescription)
}
completion(forecastArray)
}
}
task.resume()
}
It's a visual asynchronous illusion.
The static method forecast works asynchronously.
Most likely your code looks like
getForecast()
print(self.mForecastArray)
This cannot work because the array is populated much later.
Move the print line into the completion handler of the static method
func getForecast(){
Weather.forecast(withLocation: "37.8267,-122.4233") { (arr) in
DispatchQueue.main.async {
self.mForecastArray = arr
print(self.mForecastArray)
self.mTodayWeather = arr[0]
self.mCollectionView.reloadData()
}
}
}

Method to return value retrieved from HTTP request [duplicate]

This question already has answers here:
Returning data from async call in Swift function
(13 answers)
Closed 4 years ago.
I have a method which performs an HTTP request and retrieves data from a website, it's working as expected, I'm getting the data correctly. What I haven't been able to do is return the retrieved value when the method is called.
Here is the code...
func myFunction(zipCode: String)->String{
var myData:String = ""
let siteLink = "http://example.com/zip/" + zipCode
let url = URL(string: siteLink)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard error == nil else {
print(error!)
return
}
guard let data = data else {
print("Data is empty")
return
}
let json = try! JSONSerialization.jsonObject(with: data, options: [])
guard let jsonArray = json as? [[String: String]] else {
return
}
myData = jsonArray[0]["MyPropertyName"]!
// Here, myData outputs, "Info for zip code 52484 from HTTP request"
}
task.resume()
return myData
}
When I call myFunction I get and empty string...
myFunction(zipCode: "52484")// Outputs an empty string
What I was expecting to see is a return value of "Info for zip code 52484 from HTTP request" since the myData variable was modified inside the let task = before the return call. I tried returning inside the let task = but this cannot be done in Swift.
How can I return the retrieved value when the myFunction is called?
You need a completion as request is asynchronous
func myFunction(zipCode: String,completion:#escaping(_ str:String?) -> () ) {
let siteLink = "http://example.com/zip/" + zipCode
let url = URL(string: siteLink)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard error == nil else {
print(error!)
completion(nil)
return
}
guard let data = data else {
print("Data is empty")
completion(nil)
return
}
let json = try! JSONSerialization.jsonObject(with: data, options: [])
guard let jsonArray = json as? [[String: String]] else {
completion(nil)
return
}
let myData = jsonArray[0]["MyPropertyName"]!
completion(myData)
}
task.resume()
}
Call
myFunction(zipCode: "52484") { (str) in
if let st = str {
print(st)
}
}

How To Update A Label In Swift 3 With JSON Data Inside Of A Function?

For some reason whenever I try to update my label with the current temperature using self.infoLabel.text = String(temp!) inside of the DispatchQueue code block, I get the following fatal error message:
unexpectedly found nil while unwrapping an Optional value.
I'd appreciate if someone could help me figure out why the code below isn't working. Thanks.
func getCurrentTemp(city: String){
let weatherRequestURL = URL(string: "\(openWeatherMapBaseURL)?APPID=\(openWeatherMapAPIKey)&q=\(city)")!
// The data task retrieves the data.
URLSession.shared.dataTask(with: weatherRequestURL) { (data, response, error) in
if let error = error {
// Case 1: Error
print("Error:\n\(error)")
}
else {
//print("Raw data:\n\(data!)\n")
//let dataString = String(data: data!, encoding: String.Encoding.utf8)
//print("Human-readable data:\n\(dataString!)")
do {
// Try to convert that data into a Swift dictionary
let weather = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String:AnyObject]
if let main = weather["main"] as? [String: Any] {
let temp = main["temp"] as? Double
print("temp\(temp!)")
DispatchQueue.main.sync(execute: {
self.infoLabel.text = String(temp!)
})
//return temp as? String
//let temp_max = main["temp_max"] as? Double
//print("temp\(temp_max!)")
//let temp_min = main["temp_min"] as? Double
//print("temp\(temp_min!)")
}
}
catch let jsonError as NSError {
// An error occurred while trying to convert the data into a Swift dictionary.
print("JSON error description: \(jsonError.description)")
}
}
}
.resume()
}
There are two possibilities here: 1) either temp is nil (and it shouldn't be because you already force unwrap it in the print statement above) 2) or infoLabel is nil which happens if you broke your outlet connection.
Its easy to check; make a breakpoint above your assignment and in the debug console you can type:
po self.infoLabel
to see if its nil. For good measure you an also check temp.
You can also add a print statement to check self.infoLabel or an assert.
Alright, so I found a makeshift solution to this issue (See Below). Rather than placing the code inside of the function I made, I placed it in the viewDidLoad() function. For whatever reason, self.infoLabel? would be nil anywhere inside of the function I made.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print("Sucessful launched weather page.")
let weatherRequestURL = URL(string: "\(openWeatherMapBaseURL)?APPID=\(openWeatherMapAPIKey)&q=\(city)")!
// The data task retrieves the data.
URLSession.shared.dataTask(with: weatherRequestURL) { (data, response, error) in
if let error = error {
// Case 1: Error
print("Error:\n\(error)")
}
else {
//print("Raw data:\n\(data!)\n")
//let dataString = String(data: data!, encoding: String.Encoding.utf8)
//print("Human-readable data:\n\(dataString!)")
do {
// Try to convert that data into a Swift dictionary
let weather = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String:AnyObject]
if let main = weather["main"] as? [String: Any] {
let temp = main["temp"] as? Double
print("temp\(temp!)")
var tempInFarenheit = ((9/5)*((temp!)-273) + 32).rounded()
DispatchQueue.main.sync(execute: {
self.infoLabel.text = "\(tempInFarenheit) + °"
})
}
}
catch let jsonError as NSError {
// An error occurred while trying to convert the data into a Swift dictionary.
print("JSON error description: \(jsonError.description)")
}
}
}
.resume()
}
Although this isn't the most effective way of doing things, hopefully it can help others who are having the same problem. If I find a more effective way of doing this, I'll be sure to edit this post and include it.

How to make return wait until calculation is complete

thanks in advance for your help.
I'm trying to download the data from a CSV within my Dropbox. I am able to download the data successfully, but right now I'm trying to enclose/create functions out of these processes. Namely, I want create a function where I download the CSV and return the data as a string.
My issue is this though: My function returns the data variable before the download from Dropbox is complete. Therefore it doesn't return anything. i.e., I'll see the comment "returned allData variable" first, then "converted to allData variable" next. I need to reverse that somehow.
I see 2 options: 1) Figure out how to work this asynchronous bit (I'm new to Swift and have been scouring all over online to get a better understanding of it), or 2) somehow structure the return call to only execute after the data has fully downloaded.
Do you guys have any suggestions for how to move forward? I've included my code below.
func downloadFile () -> [[String:String]] {
var allData = [[String:String]]()
if let client = Dropbox.authorizedClient {
let destination : (NSURL, NSHTTPURLResponse) -> NSURL = { temporaryURL, response in
let fileManager = NSFileManager.defaultManager()
let directoryURL = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
// generate a unique name for this file in case we've seen it before
let UUID = NSUUID().UUIDString
let pathComponent = "\(UUID)-\(response.suggestedFilename!)"
print ("The path component is \(pathComponent)")
return directoryURL.URLByAppendingPathComponent(pathComponent)
}
//Option 1: Create some bit of asynchronous code here?
client.files.download(path: "/Master Test.csv", destination: destination).response { response, error in
if let (metadata, url) = response {
print("*** Download file ***")
let data = NSData(contentsOfURL: url)
let dlString = NSString(data: data!, encoding: NSUTF8StringEncoding)
let a = CSwiftV(String: String(dlString!))
allData = a.keyedRows!
print ("converted to allData variable")
//Option 2: Somehow embed the return call here?
} else {
print (error!)
}
}
}
print ("returned allData variable")
return allData
}
I recommend adding a completionHandler to your function. You can learn more about closures in the swift docs. https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html
func downloadFile (completion: (data: [[String:String]]?, error: NSError?) -> ()) -> (){
var allData = [[String:String]]()
if let client = Dropbox.authorizedClient {
let destination : (NSURL, NSHTTPURLResponse) -> NSURL = { temporaryURL, response in
let fileManager = NSFileManager.defaultManager()
let directoryURL = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
// generate a unique name for this file in case we've seen it before
let UUID = NSUUID().UUIDString
let pathComponent = "\(UUID)-\(response.suggestedFilename!)"
print ("The path component is \(pathComponent)")
return directoryURL.URLByAppendingPathComponent(pathComponent)
}
//Option 1: Create some bit of asynchronous code here?
client.files.download(path: "/Master Test.csv", destination: destination).response { response, error in
if let (metadata, url) = response {
print("*** Download file ***")
let data = NSData(contentsOfURL: url)
let dlString = NSString(data: data!, encoding: NSUTF8StringEncoding)
let a = CSwiftV(String: String(dlString!))
allData = a.keyedRows!
print ("converted to allData variable")
//Option 2: Somehow embed the return call here?
completion(data: allData, error: nil)
} else {
print (error!)
completion(data: nil, error: error)
}
}
}
print ("returned allData variable")
return allData
}
Function call
downloadFile(){
//start your loader here
dropboxData in
//remove here
if(dropboxData.error == nil){
print(dropboxData.data)
}else{
print(dropboxData.error)
}
}