Resume Data from URLSessionDownloadTask Always nil - swift3

i have an app which i need to download the file from the internet when i downloading the file it's work good but my problem is when i pressed pause button to pause the downloading for one minute or more i get nil from resume Data
the following my code :
#IBAction func startDownloading(_ sender: UIButton)
{
isDownload = true
sessionConfig = URLSessionConfiguration.default
let operationQueue = OperationQueue.main
session = URLSession.init(configuration: sessionConfig, delegate: self, delegateQueue: operationQueue)
let url = URL(string: "www.example.com")
downloadTask = session.downloadTask(with: url!)
downloadTask.resume()
}
#IBAction func pause(_ sender: UIButton)
{
if downloadTask != nil && isDownload
{
self.downloadTask!.cancel(byProducingResumeData: { (resumeData) in
// here is the nil from the resume data
})
isDownload = false
downloadTask = nil
pasueBtnOutlet.setTitle("Resume", for: .normal)
}
if !isDownload && downloadData != nil
{
downloadTask = session.downloadTask(withResumeData: downloadData as Data)
downloadTask.resume()
isDownload = true
downloadData = nil
pasueBtnOutlet.setTitle("Pause", for: .normal)
}
}
please can help me
thanks for all

Your code seems to be correct, you just need to init the downloadData with resumeData in closure
Make a property of downloadData
var downloadData:Data!
and then in your pause button action where you cancel the task, set the downloadData with resumeData
self.downloadTask!.cancel(byProducingResumeData: { (resumeData) in
// here is the nil from the resume data
// You have to set download data with resume data
self.downloadData = resumeData
})
In order to check the progress and completion, implement these URLSessionDownloadDelegate delegates
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
print(Int(progress * 100))
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("Downloading done")
}
NOTE:- Try a valid downloadable url. For example
http://www.star.uclan.ac.uk/news_and_events/news/2010020901/sdo_resolution_comparison.png
Its http, make sure to set Transport Security in Info.plist

Related

SwiftUI combine nil data

I have created a class to perform a network request and parse the data using Combine. I'm not entirely certain the code is correct, but it's working as of now (still learning the basics of Swift and basic networking tasks). My Widget has the correct data and is works until the data becomes nil. Unsure how to check if the data from my first publisher in my SwiftUI View is nil, the data seems to be valid even when there's no games showing.
My SwiftUI View
struct SimpleEntry: TimelineEntry {
let date: Date
public var model: CombineData?
let configuration: ConfigurationIntent
}
struct Some_WidgetEntryView : View {
var entry: Provider.Entry
#Environment(\.widgetFamily) var widgetFamily
var body: some View {
VStack (spacing: 0){
if entry.model?.schedule?.dates.first?.games == nil {
Text("No games Scheduled")
} else {
Text("Game is scheduled")
}
}
}
}
Combine
import Foundation
import WidgetKit
import Combine
// MARK: - Combine Attempt
class CombineData {
var schedule: Schedule?
var live: Live?
private var cancellables = Set<AnyCancellable>()
func fetchSchedule(_ teamID: Int, _ completion: #escaping (Live) -> Void) {
let url = URL(string: "https://statsapi.web.nhl.com/api/v1/schedule?teamId=\(teamID)")!
let publisher = URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: Schedule.self, decoder: JSONDecoder())
//.catch { _ in Empty<Schedule, Error>() }
//.replaceError(with: Schedule(dates: []))
let publisher2 = publisher
.flatMap {
return self.fetchLiveFeed($0.dates.first?.games.first?.link ?? "")
}
Publishers.Zip(publisher, publisher2)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: {_ in
}, receiveValue: { schedule, live in
self.schedule = schedule
self.live = live
completion(self.live!)
WidgetCenter.shared.reloadTimelines(ofKind: "NHL_Widget")
}).store(in: &cancellables)
}
func fetchLiveFeed(_ link: String) -> AnyPublisher<Live, Error /*Never if .catch error */> {
let url = URL(string: "https://statsapi.web.nhl.com\(link)")!
return URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: Live.self, decoder: JSONDecoder())
//.catch { _ in Empty<Live, Never>() }
.eraseToAnyPublisher()
}
}
Like I said in the comments, it's likely that the decode(type: Live.self, decoder: JSONDecoder()) returns an error because the URL that you're fetching from when link is nil doesn't return anything that can be decoded as Live.self.
So you need to handle that case somehow. For example, you can handle this by making the Live variable an optional, and returning nil when link is empty (or nil).
This is just to set you in the right direction - you'll need to work out the exact code yourself.
let publisher2 = publisher1
.flatMap {
self.fetchLiveFeed($0.dates.first?.games.first?.link ?? "")
.map { $0 as Live? } // convert to an optional
.replaceError(with: nil)
}
Then in the sink, handle the nil:
.sink(receiveCompletion: {_ in }, receiveValue:
{ schedule, live in
if let live = live {
// normal treatment
self.schedule = schedule
self.live = live
//.. etc
} else {
// set a placeholder
}
})
SwiftUI and WidgetKit work differently. I needed to fetch data in getTimeline for my IntentTimelineProvider then add a completion handler for my TimelineEntry. Heavily modified my Combine data model. All credit goes to #EmilioPelaez for pointing me in the right direction, answer here.

Swift 3, want to perform timeout when using URLSession to load image from server

I'm using swift 3. I want to add timeout to URLSession when doing download task. I did use configuration to change my setting, however, it doesn't work. The code didn't perform timeout... If the server didn't response quickly, it will fail.
Here is my code:
import Foundation
import UIKit
extension UIImageView {
func loadImage(url: URL) -> URLSessionDownloadTask {
let session: URLSession = {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 70
configuration.timeoutIntervalForResource = 70
return URLSession(configuration: configuration, delegate: nil, delegateQueue: nil)
}()
let downloadTask = session.downloadTask(with: url, completionHandler: { [weak self] url, response, error in
if error == nil, let url = url, let data = try? Data(contentsOf: url), let image = UIImage(data: data) {
DispatchQueue.main.async {
if let strongSelf = self {
strongSelf.image = image
}
}
}
})
downloadTask.resume()
return downloadTask
}
}
Any comment is appreciated!!

How to pass image data of AVCaptureSession captured image to another view controller?

I made a camera app using AVFoundation's AVCaptureSession and I wanted to pass the data of the captured image to another view controller.
This is the function where the capture os being handled:
func capturePhoto() {
self.counter += 1
if let videoConnection = sessionOutput.connection(withMediaType: AVMediaTypeVideo) {
videoConnection.videoOrientation = .landscapeRight
sessionOutput.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (buffer, error) in
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer)
let data = NSData(data: imageData!)
// append the data to a global variable
DispatchQueue.main.async {
self.imageData?.append(data)
}
let image = UIImage(data: imageData!)!
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
})
}
if self.counter == self.count {
self.timer?.invalidate()
self.timer = nil
self.counter = 0
self.shutterTimer.isHidden = true
self.shutterTimer.layer.removeAllAnimations()
// if the condition is met, then go to another vc with the image data
setupFrameAndSave()
}
}
this is the function that handles the segue to another view controller with the image data:
func setupFrameAndSave() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "saveVC") as! SaveVC
controller.imageData = self.imageData
self.present(controller, animated: true, completion: nil)
}
Now, the problem is that the data that is being passed prints nil and placing the appending data to array inside DispatchQueue.main.async didn't do anything.

Local Notificaton not working in simulator iOS10.0

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
askForPermission()
}
#IBAction func addLocalNotification(_ sender: AnyObject) {
addLocalNotification()
}
func addLocalNotification() {
let content = UNMutableNotificationContent()
content.title = "iOS10.0"
content.body = "Hello Buddy"
content.sound = UNNotificationSound.default()
// Deliver the notification in five seconds.
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 5, repeats: false)
let request = UNNotificationRequest.init(identifier: "FiveSecond", content: content, trigger: trigger)
// Schedule the notification.
let center = UNUserNotificationCenter.current()
center.add(request) { (error) in
print(error)
}
print("should have been added")
}
func askForPermission() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound,.badge]) { (granted, error) in
}
}
You have to implement a delegate to UNUserNotificationCenter to tell the system you want to display the notification while the app is running. See the sample here: https://github.com/jerbeers/DemoLocalNotification

Swift 3 and NSURLSession issue

Thanks to Apple my iOS 9 Project 'Swift 2.3' is completely unusable with iOS 10's 'Swift 3'...
I fixed almost everything except that I am having issue with using NSURLSession, Xcode is telling me that it has been renamed to URLSession, if I rename it Xcode will tell me:
use of undeclared type URLSession
Foundation is imported.
What is the issue?!
For example I am using it this way...
lazy var defaultSession: URLSession = {
let configuration = URLSessionConfiguration.background(withIdentifier: "reCoded.BGDownload")
configuration.sessionSendsLaunchEvents = true
configuration.isDiscretionary = true
let session = URLSession(configuration: configuration, delegate: self, delegateQueue, queue: nil)
return session
}()
and even with the delegate methods the same issue.
Try using Foundation.URLSession where ever you use URLSession.
/Got it to work/ In some cases try to copy your code somewhere else then remove everything in your class that uses URLSession then type the session methods again and put back your copied code you should be fine.
Update your URLSessin functions with;
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
self.data.append(data as Data)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON()
}
}
I can explain how but by playing around with the code I got this to work in SWIFT 3 after two days of frustration. I guess SWIFT 3 removed a lot of unnecessary words.
let task = Foundation.URLSession.shared.dataTask(with: <#T##URL#>, completionHandler: <#T##(Data?, URLResponse?, Error?) -> Void#>)
Here's where I am right now. It's not perfect but works maybe half of the time.
First, in the class where my URLsession is defined:
import Foundation
class Central: NSObject, URLSessionDataDelegate, URLSessionDelegate, URLSessionTaskDelegate, URLSessionDownloadDelegate {
I don't think all of that is necessary, but there it is. Then here is the function that is called by my background fetch:
func getWebData() {
var defaults: UserDefaults = UserDefaults.standard
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: "myBGconfig")
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
urlString = "https://www.powersmartpricing.org/psp/servlet?type=dayslider"
if let url = URL(string: urlString) {
let rateTask = backgroundSession.downloadTask(with: URL(string: urlString)!)
rateTask.taskDescription = "rate"
rateTask.resume()
}
When the task comes back:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL ) {
if downloadTask.taskDescription == "rate" { // I run 2 web tasks during the session
if let data = NSData(contentsOf: location) {
var return1 = String(data: data as! Data, encoding: String.Encoding.utf8)!
DispatchQueue.global(qos: .userInteractive).asyncAfter(deadline: .now() + 0.2){
var defaults: UserDefaults = UserDefaults.standard
defaults.set(myNumber, forKey: "electricRate") // myNumber is an extract of the text in returned web data
defaults.set(Date(), forKey: "rateUpdate")
defaults.synchronize()
self.calcSetting() //Calls another function defined in the same class. That function sends the user a notification.
let notificationName = Notification.Name("GotWebData")
NotificationCenter.default.post(name: notificationName, object: nil)
} // Closes the Dispatch
}
if session.configuration.identifier == "myBGconfig" {
print("about to invalidate the session")
session.invalidateAndCancel()
}
}
I haven't figured out yet how to kill the session when BOTH tasks have completed, so right now I kill it when either one is complete, with invalidateAndCancel as above.
And finally, to catch errors:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didCompleteWithError: Error?) {
if downloadTask.taskDescription == "rate" {
print("rate download failed with error \(didCompleteWithError)")
}
if downloadTask.taskDescription == "other" {
print("other download failed with error \(didCompleteWithError)")
}
downloadTask.resume() // I'm hoping this retries if a task fails?
}
func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
if let error = error as? NSError {
print("invalidate, error %# / %d", error.domain, error.code)
} else {
print("invalidate, no error")
}
}