I'm developing a resume function for file downloads. It looks good for the most of it (getting the resume data of the cancelled request, etc.), but when I'm creating the request for resuming the download, its URL is nil.
The code for creating the request is pretty straightforward:
let downloadRequest = sessionManager.download(resumingWith: resumableData)
The downloadRequest object has a URLRequest variable but it is get-only.
I've read the doc but found no answer there.
Please note that the first request (the one that was cancelled) was created using this code:
let dataRequest = sessionManager.request(urlRequest)
How is the DownloadRequest object getting its url? Am I missing something obvious?
The answer lies in the first request made. When first starting to download the file, even if you don't plan to resume it later, you need to create an Alamofire.DownloadRequest (URLSessionDownloadTask) and not an Alamofire.DataRequest (URLSessionDataTask). The rest is the same: you capture the resume data when it fails (or if you cancel it by producing resume data), then you create the next request with that data.
Hope this helps!
Related
I'm need to implement the common scenario of having a table view with N cells where each of those cells needs to download an image to be displayed within it.
The service protocol to call to download the images could be either HTTP or HTTPS.
I am using an URLSessionDownloadTask this way:
func downloadImage(urlStr: String, completion: #escaping (UIImage?, Error?) -> Void) {
let url = URL(string: urlStr)
let request = URLRequest(url: url!)
let task = session.downloadTask(with: request, completionHandler: {
(fileUrl, response, error) in
// Call 'completion' depending on result
})
task.resume()
}
Where session is an URLSession with default configuration and associated operation queue:
self.session = URLSession(configuration: configuration, delegate: nil, delegateQueue: self.operationQueue)
So, what I want is to avoid to download an image that was already downloaded. And I would like them to have some expiration time.
I've read some articles and posts and I'm not completely clear about the differences between the options I found:
A. Using FileManager to actually store the image as a file, and removing it after checking the expiration time.
B. Setting the cachePolicy property of URLRequest.
C. Using URLCache
D. Using NSCache
About A:
What is actually the difference between storing the image as file and using a cache? Could have the file storage offer any benefit that a cache does not? Those images are not user-related, I can download them from a server when needed.
About B:
I read Apple's documentation about that, but I don't fully understand if for my scenario I should use NSURLRequestUseProtocolCachePolicy.
How does this option actually work? It is enough to set the policy and then you don't have to care about anything else? How does the URLRequest now that the image is asked for download has been already downloaded and cached?
About C:
How does it should be correctly implemented? Could anybody provide me an example/tutorial in case this is the best approach? What about expiration date?
About D:
I found an example I understood, but would it be a good approach having the previous options? What about expiration date also here?
In summary: which of the options would be the most efficient and appropriate for my scenario, and why?
From what I inferred about your question "what I want is to avoid to download an image that was already downloaded. And I would like them to have some expiration time."
To avoid images from downloading again, you can implement the following use case where, you store the images in the NSCache using the urls of the image itself.
This would be something like as discussed in the link.
For the expiration time case, if you want to remove all images at a particular expiration time, then just make a check for that scenario and empty the cache.
For the case where you want to remove the individual images, based on their expiration time, you can check the response from the server for the expiration key, and again remove cache in case the limit has been breached.
1. Clicking the link causes a database update.
There is a certain link I have access to (let's pretend it's www.google.com), such that when I open it up in my browser, it updates a certain section of the JSON code in my database. Based on the numbers that make up a portion of the link, it adjusts a certain value in the data.
2. How do I run this link in the background of my iOS app?
I need to be able to "open" this link within the app, without actually opening up a UIWebview and visually visiting the site. I just need this JSON data inside the database to update on its own (with the user unaware that it even happened).
The problem I'm having here is that I simply don't know how this is done. How do I cause this link to be visited without opening up a Safari browser?
The best approach I've found for such functions is to treat them as if they were "AJAX" (or "REST", "API", etc.) - while these terms are often used (and for more seasoned programmers instantly give a certain thought), the end result is that they take information from your 'originator' and send to the 'server' for processing, which then replies with a 'response'. Once you get that concept in your head, this becomes a fairly simple activity.
(for our example, I will call this "API", as that does really suit {as #Mortiz suggested} this question best)
For Swift 3, there are several ways to do this, I'll show you two I've found and use for various functions:
DispatchQueue
For a 'one-time shot to a url that I know exists and will connect reliability', this is a good one to use (think of it as a 'quick-n-dirty' if you like....!)
DispatchQueue.global().async {
let data = try? Data(contentsOf: theURL!) //make sure your url does exist, otherwise unwrap in a if let check / try-catch
DispatchQueue.main.async {
// do stuff here with the data if you need
// you can get the response from the server and parse it out, set buttons in the app, etc.
}
}
Alamofire
For Swift 3, Alamofire is extremely popular and does a lot of great stuff. Check it out if you haven't already!
Alamofire.request("\(theURL!)").responseJSON { response in
print("result is ", response.result)
switch response.result {
case .success(let value):
// do stuff with the returned data
// like updating your internal database, etc.
print(value)
case .failure(let error):
print("There was an error")
// you can see the error response in various ways....
print(requested)
print(error)
print(response)
print(response.result)
}
}
Once you have your buttons in place (from your description it sounds like that is what your #1 is about), then in the function you call when it is clicked, drop in the code from above and 'do stuff' as you need.
This will make the update to the server automatically in the background (answering your #2) - the user won't notice anything unless there are connection issues to the internet, etc. (much too complex to get into here, though if you expect to have much of it, Alamofire is a great choice as it automatically retries, etc. (part of the great features you should check out)
A key piece of this is that you can take the response from the URL (send various bits of JSON data back from the server, then break it back down in the phone) and do 'whatever' with it.
Some things you can do (to hopefully give you more ideas - which is just about anything.....):
update data in the app (local storage, local variables, etc.)
update text (color, background) inside Buttons or Labels
process Alerts to the user (not your case, but sometimes you want to let them know what went on - certainly if it was an error in updating your server)
change Images (various things)
switch Views
Well, the list is as long as "things you can do in an app", so decide for yourself what you need to mod/update - this is "the" way to do it!
You could also use the UIWebView without ever showing it, like this (Swift 3):
func webView() {
let theWebView: UIWebView
theWebView = UIWebView(frame: UIScreen.main.bounds)
theWebView.delegate = self
if let theURL = URL(string: "your URL") {
let request = URLRequest(url: theURL)
theWebView.loadRequest(request)
}
}
Just don't add it to the view.
I'm building an app for my school which includes a lunch menu, however since the lunch menu changes every month I don't want to keep updating my app just for that. I know you can open PDFs with Xcode either locally or online, but is there a way to change the url path of the pdf manually and have it update real time within the app. Any help would be much appreciated and I'm new to developing IOS apps, so any links that may help would be great.
If you control the website or have a line of communication with the person who does then you could agree that the latest menu always gets returned from example.com/current/lunch.pdf but really this should be set up as a GET request example.com/index.php?lunchmenu=current so that it is down to the website to respond with the file or the file location rather than the app's responsibility to guess the filename. So for example:
if let url = NSURL(string: "http://www.example.com/index.php?lunchmenu=current") {
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let dataTask = session.dataTaskWithURL(url, completionHandler: {(d,_,_) in
if let data = d {
String(data:data, encoding:NSUTF8StringEncoding) // this is could be your file name
}
})
dataTask.resume()
}
It would then be possible to perform other requests like: example.com/index.php?lunchmenu=nextmonth. Working in this way means that if for some reason the naming pattern or location of the files changed your app would still keep working.
If you don't have access to the school website but do know where the file is stored, you could query your own website for the file information and either update this automatically (or if there was no other way - manually). Again, this would prevent an app update if the structure of the school website changed.
Only rely on static file locations and naming systems if you really have to.
I am trying to read data from my API. I am not using JSON data because the API doesn't return an array, just a line of text. Anyways, I am using the following code to read the text from the API.
func contactVetApi(url:String){
let nsUrl = NSURL(string:url)
let task = NSURLSession.sharedSession().dataTaskWithURL(nsUrl!){
(data, response, error) in
print(data)
}
task.resume()
}
I am calling this function in the ViewDidLoad function of my ViewController file. As you can see, it takes a parameter that is a string. The parameter is the URL to read. It then translates the string into a NSUrl so it can be used with the sharedSession. I then initialize the shared session and create a data task with that url. I then print out the data it returns. The only issue is that the output isn't what I am expecting. What I am expecting is for it to say, "Future home of something quite cool." Although, this is what I am getting.
Optional(<46757475 72652068 6f6d6520 6f662073 6f6d6574 68696e67 20717569 74652063 6f6f6c>)
Optional(<46757475 72652068 6f6d6520 6f662073 6f6d6574 68696e67 20717569 74652063 6f6f6c>)
I need help figuring out why it is printing that out instead of what I am expecting. In case it is needed, the api url is http://apis.wilsonfamily5.org/vet/about.php. Before anybody asks though, I did add into the info.plist file the disabling of the iOS 9 app transport security. If you need any extra information to help me solve this problem, I would be more then happy to give it to you. I want to thank you in advance.
You currently are printing a NSData object, which will always look like that jibberish. What you actually want however is to convert the NSData to a NSString or String to create a human readable form:
var dataAsString = NSString(data: data, encoding: NSUTF8StringEncoding)
Taken from this answer.
I have found many suggestion in stack overflow regarding usage of FFmpeg and link of github for DFURTSPPlayer but it is not compiling. But after integrating FFmpeg what I have to write? suppose i am having HTTP urls then I write:
code
moviePath = "http:/path.mp4"
movieURL = NSURL.URLWithString(moviePath!)
moviePlayer = MPMoviePlayerController(contentURL: movieURL)
moviePlayer!.play()
So for using RTSP urls what kind of code should i write?
Here is another post that has an example FFmpeg code that receives an RTSP stream (this one also decodes the stream to YUV420, stores it in pic, then converts the frame to RGB24, stores in picrgb and writes it to a file). So to achieve something similar to what you have for HTTP you should:
1) Write a wrapper Objective-C class for the FFmpeg C code, or just wrap the code in functions/functions that you will call directly from Objective-C code. You should have a way to pass the RTSP url to the class or function and provide a callback for a new frame. In the class/function start a new thread that will actually execute something similar to the code in the example and call a callback for each new decoded frame. NOTE: FFmpeg has a way to perform asynchronous I/O by using your own custom IO context and that would actually allow you to avoid creating the thread, but if you are new to FFmpeg maybe start with the basics and then you can improve your code later on.
2) In the callback update the view or whatever you are using for display with the decoded frame data.