Posts

Post marked as solved
15 Replies
I was sure that I've posted here the solution, but better later than never... Since it's a pretty long code, I've made a gist for it (hopefully it doesn't missing any custom helper): https://gist.github.com/Idomo/ece6683348372b54e470fd630b1fb76e Beside that, to recreate the saved unfinished sessions, so it'll fire urlSession(_:task:didCompleteWithError:) and re-upload, you should call this function in application(_:, didFinishLaunchingWithOptions:): func recreateFilesUploadTasks() { if let filesUploadTasks = UserDefaults.standard.stringArray(forKey: UserDefaultsKeys.filesUploadTasks) { let filesManager = FilesManager() for sessionId in filesUploadTasks { filesManager.recreateSession(id: sessionId) } } }
Post marked as solved
15 Replies
Got it, but I'm missing the answer for "Where should I recreate it and how?"Also, I'm not sure that I understood this:It’s common for folks to make the mistake of creating their URLSession lazily, either deliberately (as an optimisation) or accidentally (because they’ve tied the session to a view controller, which is a really bad idea).What do you mean by "creating URLSession lazily"?How can I create it in another way? I know that I need to create it only after some action the user performed, so it will always be "lazily" (or I didn't got it correctly).I'm creating it and attaching the delegate to a class that I've created (class FilesManager: NSObject, URLSessionDataDelegate, URLSessionDelegate{}).If not using `self` for the delegate, what else can I use?How can I pass delegate as some "`static class`" so it will always be avaliable to fire delegate's methods?Sorry for asking so much, but I couldn't found answers for this thing anywhere and the docs are missing most of the things you've mentioned since your first comment.
Post marked as solved
15 Replies
I saw your post and also tried to use `os_log` after one of your comments, but it didn't worked for me from some reason so I switched to `NSLog`.What do you mean by "Are you user you re-created your background session?" where should I recreate it and how? as much as I understood it should call the delegate automatically, 'cause it is a background process that can't "die" (even after force quit).
Post marked as solved
15 Replies
OK, thats a good direction.Can I retry to execute the `uploadTask` from the delegate?Will calling `resume()` on `task: URLSessionTask` work or after the task has been canceled it useless?Edit:It doesn't seems to fire `urlSession(session:task:didCompleteWithError:)` after opening the app after force quitting it (I'm able to see `NSLog` output from other methods, but not from the delegate),I can't even log the error to see if it contains the keys you've mentioned.You may see my logs:16:47:19.249330 +0300 My app Debug: viewDidLoad() 16:51:57.128801 +0300 My app Debug: uploadImage(_:to:completionBlock:), Upload task resumed 16:52:07.594877 +0300 My app Debug: urlSession(_:task:didCompleteWithError:) 16:52:40.115190 +0300 My app Debug: uploadImage(_:to:completionBlock:), Upload task resumed ### Force Quit here, not getting to `urlSession(_:task:didCompleteWithError:)` ### Open the app again: 16:52:49.592161 +0300 My app Debug: viewDidLoad()
Post marked as solved
15 Replies
After some searching I found that background uploads works only when uploading from file, so I saved the `URLRequest.httpBody` as a file and uploaded it:static func urlSessionConfiguration(for id: String) -> URLSessionConfiguration { let configuration = URLSessionConfiguration.background(withIdentifier: FilesManager.urlSessionIdentifierPrefix + id) configuration.sharedContainerIdentifier = FilesManager.urlSessionIdentifierPrefix return configuration } let session = URLSession(configuration: FilesManager.urlSessionConfiguration(for: fileName), delegate: self, delegateQueue: nil) let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] let filePath = "\(documentsPath)/\(fileName)" DispatchQueue.main.async { if let data = urlRequest.httpBody, (data as NSData).write(toFile: filePath, atomically: true) { urlRequest.httpBody = nil // Use only file's content let fileUrl = NSURL(fileURLWithPath: filePath) as URL session.uploadTask(with: urlRequest, fromFile: fileUrl).resume() } else { session.uploadTask(withStreamedRequest: urlRequest).resume() } }Now I'm facing an issue that if the user is manually quit the app (from the app switcher), the background uploads are cancled (that I've found in some article: "if you manually force quit the app then iOS will take this as definite confirmation that you are finished with the app and so cancel any scheduled background-transfers.").Well, as one of many users how quiting apps like this when we were done with (or at least we're think so, 'cause we're not aware to background tasks), I can tell that it is a really big issue.The article I've mentioned does suggest a way to "overcome" this issue, but it only for testing, I can't really detect when the app has been quit from the app switcher (as much as I know).So my question is:Can I prevent from background tasks to be cancelled when the app is quit from the app switcher (or at least to detect that from `AppDelegate` and run `exit()` from code to overcome this)?
Post marked as solved
15 Replies
It really depends on the resolution of the image, it can be between 0.1Mb to 1MB (maybe a bit more).Anyway, I got some suggestion that based on what you've said (that only GET requests are retrying to be sent 'till success), and tried to send some `ping` with GET (tried also with HEAD) request so only when `urlSession(_:task:didCompleteWithError:)` will beign called, I'll send the POST request (and know that there is an internet connection), but from some reason I'm getting timeout also for the GET request (that uses the same background configuration).Please tell me, am I doing something wrong?This is my code:In class:static var requests = [URLRequest]() static let urlSessionConfiguration: URLSessionConfiguration = { let configuration = URLSessionConfiguration.background(withIdentifier: FilesManager.urlSessionIdentifier) configuration.sessionSendsLaunchEvents = false configuration.sharedContainerIdentifier = "myApp" return configuration }() In upload func:let url = URL(string: "https://url.com/path")! var urlRequest = URLRequest(url: url) urlRequest.httpMethod = "POST" urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") urlRequest.httpBody = body FilesManager.requests.append(urlRequest) URLSession(configuration: FilesManager.urlSessionConfiguration, delegate: self, delegateQueue: nil).dataTask(with: URL(string: "https://url.com/ping.txt")!).resume()Delegate:extension FilesManager: URLSessionDataDelegate, URLSessionDelegate { func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { print("\(#funcion) \(error), \(task.originalRequest?.httpMethod)") guard FilesManager.requests.count > 0 else { return } let urlRequest = FilesManager.requests.removeFirst() FilesManager.urlSession.dataTask(with: urlRequest).resume() } }Console:urlSession(_:task:didCompleteWithError:) Optional(Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={_kCFStreamErrorCodeKey=-2104, _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundDataTask .<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=( "BackgroundDataTask .<1>", "LocalDataTask .<1>" ), NSLocalizedDescription=The request timed out., _kCFStreamErrorDomainKey=4, NSErrorFailingURLStringKey=https://url.com/ping.txt, NSErrorFailingURLKey=https://url.com/ping.txt}), Optional("GET") urlSession(_:task:didCompleteWithError:) Optional(Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={_kCFStreamErrorCodeKey=-2104, _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundDataTask .<2>, _NSURLErrorRelatedURLSessionTaskErrorKey=( "BackgroundDataTask .<2>", "LocalDataTask .<2>" ), NSLocalizedDescription=The request timed out., _kCFStreamErrorDomainKey=4, NSErrorFailingURLStringKey=https://url.com/path, NSErrorFailingURLKey=https://url.com/path}), Optional("POST")
Post marked as solved
15 Replies
Oh, so do you have another way to achieve this goal? (I have to use POST 'cause I'm using `multipart/form-data`)
Post not yet marked as solved
7 Replies
The only sulotion I could find is to downgrade to xCode 11 beta 2 (link to download) that doesn't have this annoying issue.Hope that they would solve this before the last realese.
Post not yet marked as solved
6 Replies
That’s what I’m saying, the only way to see the storyboard file is with the source code editor and every little change there causing error message that xCode can’t open the storyboard, it doesn’t even makes it to the death wheel part...
Post not yet marked as solved
6 Replies
Hi, thank you for comment here.How can I check IBOutlets connection if I don't have access to the storyboard?There is a lot of code and if the storyboard is crashing I have to quit xCode and open it again after deleteing some file that I've specified in the first comment, It will take forever to check all of the code in my project...I still think this is an issue in the .storyboard file itself, but I don't know what can cause that in there and I can't comment anything there, otherwise it will show an error that the storyboard can't be opened.
Post not yet marked as solved
3 Replies
The post you've attached isn't simillar to my issue, it says: "but I am able to click on a specific view and still can see their properties defined through attributes inspector", while my storyboard isn't loading at all, all I can see is the ActivityIndicator loading forever with the death wheel (colorful endless spinning wheel).Also, the one from Apple's staff said that the solution there shouldn't be done and it doesn't really solving the issue.Yes, it is duplicate to get the most attention that it can, if someone will see one of the threads, they may not see the other so at least one of them will get an answer.I know that the issue is related to the storyboard source code, when I've tried to comment some code there (accidentally with /* */), the story board showed up, but crashed becaused of the incorrect comment-way.May you look at the code in the link that I've attached at the bottom of the first comment?