I need to check if a website is reachable before loading it. I am new in iOS developement but this is the method I've implemented to discover the response.
var url = NSURL(string: "http://www.apple.com")
var task = NSURLSession.sharedSession().dataTaskWithURL(url!) {
data, response, error in
println(data)
var httpResponse = response as? NSHTTPURLResponse
println(httpResponse)
}
task.resume()
It works! But the problem is that the response comes from the cache... So the result is that:
If I am checking if a file exists and at that moment I am checking it exists -> for the application it will always exist because it is stored in the cache... So if I remove the file and then I make the request... it will always give me response 200 and not 404.
Infact if I insert this line of code (it deletes the cache!) before making the request then it works like it should work and it always check for real if the website or the file exists!
NSURLCache.sharedURLCache().removeAllCachedResponses()
So... how can I solve this problem in Swift?...thank you very much
You can set a no cache policy by using a new url session instance.
Create a property and set a new NSURLSession instance to it.
var urlSession : NSURLSession!
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.requestCachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData;
self.urlSession = NSURLSession(configuration: configuration)
Use this URLSession property to get your data.
var url = NSURL(string: "http://www.apple.com")
var task = self.urlSession.dataTaskWithURL(url!) {
data, response, error in
// Your code
}
task.resume()
Related
I'm looking for a Swift 5 version of something like the fileExists function (below). For context I have an app that is heavily dependent on interaction with a remote server. For resilience we have created a second remote server and want the app to check if the first one is available, and if not, use the second one. I have found possible solutions using URLSession.shared.dataTask but they all just display a message rather than return an alternative url to use. Any suggestions would be most welcome.
func fileExists(url : NSURL!) -> Bool {
let req = NSMutableURLRequest(URL: url)
req.HTTPMethod = "HEAD"
req.timeoutInterval = 1.0 // Adjust to your needs
var response : NSURLResponse?
NSURLConnection.sendSynchronousRequest(req, returningResponse: &response, error: nil)
return ((response as? NSHTTPURLResponse)?.statusCode ?? -1) == 200
}
When attempting an http request to my rest api, I continually get a 401 error when using the following code. I don not get this error making any other type of request. I have provided the function that makes the request below.
func deleteEvent(id: Int){
eventUrl.append(String(id))
let request = NSMutableURLRequest(url: NSURL(string: eventUrl)! as URL)
request.httpMethod = "DELETE"
print(eventUrl)
eventUrl.removeLast()
print(self.token!)
request.allHTTPHeaderFields = ["Authorization": "Token \(self.token)"]
let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
if error != nil {
print("error=\(String(describing: error))")
//put variable that triggers error try again view here
return
}
print("response = \(String(describing: response))")
}
task.resume()
}
When sending the delete request with postman, the rest api just returns the data I want to delete but does not delete it. For reference I have posted the view and permissions classes associated with this request Any help understanding why this may be resulting in an error is greatly appreciated!
Views.py
class UserProfileFeedViewSet(viewsets.ModelViewSet):
"""Handles creating, reading and updating profile feed items"""
authentication_classes = (TokenAuthentication,)
serializer_class = serializers.ProfileFeedItemSerializer
queryset = models.ProfileFeedItem.objects.all()
permission_classes = (permissions.UpdateOwnStatus, IsAuthenticated)
def perform_create(self, serializer):
"""Sets the user profile to the logged in user"""
#
serializer.save(user_profile=self.request.user)
Permissions.py
class UpdateOwnStatus(permissions.BasePermission):
"""Allow users to update their own status"""
def has_object_permission(self, request, view, obj):
"""Check the user is trying to update their own status"""
if request.method in permissions.SAFE_METHODS:
return True
return obj.user_profile.id == request.user.id
HEADER SENT WITH DELETE REQUEST VIA POSTMAN
Preface: You leave out too much relevant information from the question for it to be properly answered. Your Swift code looks, and please don't be offended, a bit beginner-ish or as if it had been migrated from Objective-C without much experience.
I don't know why POSTMAN fails, but I see some red flags in the Swift code you might want to look into to figure out why your iOS app fails.
I first noticed that eventUrl seems to be a String property of the type that contains the deleteEvent function. You mutate it by appending the event id, construct a URL from it (weirdly, see below), then mutate it back again. While this in itself is not necessarily wrong, it might open the doors for racing conditions depending how your app works overall.
More importantly: Does your eventUrl end in a "/"? I assume your DELETE endpoint is of the form https://somedomain.com/some/path/<id>, right? Now if eventUrl just contains https://somedomain.com/some/path your code constructs https://somedomain.com/some/path<id>. The last dash is missing, which definitely throws your backend off (how I cannot say, as that depends how the path is resolved in your server app).
It's hard to say what else is going from from the iOS app, but other than this potential pitfall I'd really recommend using proper Swift types where possible. Here's a cleaned up version of your method, hopefully that helps you a bit when debugging:
func deleteEvent(id: Int) {
guard let baseUrl = URL(string: eventUrl), let token = token else {
// add more error handling code here and/or put a breakpoint here to inspect
print("Could not create proper eventUrl or token is nil!")
return
}
let deletionUrl = baseUrl.appendingPathComponent("\(id)")
print("Deletion URL with appended id: \(deletionUrl.absoluteString)")
var request = URLRequest(url: deletionUrl)
request.httpMethod = "DELETE"
print(token) // ensure this is correct
request.allHTTPHeaderFields = ["Authorization": "Token \(token)"]
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print("Encountered network error: \(error)")
return
}
if let httpResponse = response as? HTTPURLResponse {
// this is basically also debugging code
print("Endpoint responded with status: \(httpResponse.statusCode)")
print(" with headers:\n\(httpResponse.allHeaderFields)")
}
// Debug output of the data:
if let data = data {
let payloadAsSimpleString = String(data: data, encoding: .utf8) ?? "(can't parse payload)"
print("Response contains payload\n\(payloadAsSimpleString)")
}
}
task.resume()
}
This is obviously still limited in terms of error handling, etc., but a little more swifty and contains more console output that will hopefully be helpful.
The last important thing is that you have to ensure iOS does not simply block your request due to Apple Transport Security: Make sure your plist has the expected entries if needed (see also here for a quick intro).
Currently I have an iOS app that pulls prices and data from websites. So far its been working well, but I want to make it more accurate. To do so, I need to set the cookies for the URL request that I'm currently using String(contentsOf: _) for.
Current Process
let requestUrl: URL = URL(string: "http://www.samsclub.com/sams/search/searchResults.jsp?searchTerm=Apple")!
var content: String?
do {
content = try String(contentsOf: requestUrl)
} catch {
print("Error while converting an NSURL to String: \(error)")
}
if content != "" {
// I do things with the content of the requestUrl...
}
Could Use?
I thought that maybe I should use Alamofire instead to pull those website, and then parse the data.
I need to set the cookie that changes the store number to search, but have been unable to find a way to do so. Bellow is the code I have for pulling the websites data without setting a cookie.
let requestUrl: String = "http://www.samsclub.com/sams/search/searchResults.jsp?searchTerm=Apple"
Alamofire.request(requestUrl, method: .post).responseString { response in
if let content: String = response.result.value {
// I do things with the content of the requestUrl...
}
}
Other Claims
I have found many different ways to set cookies through Alamofire that don't work, but if Alamofire isn't the way to do it, please inform me. I really need this to work, and I'm open to any and every suggestion.
It took four weeks to the day, but I figured it out! URLRequest and Alamofire were my glorious answers!
Create the URL to call.
let requestUrl: String = "http://www.samsclub.com/sams/search/searchResults.jsp?searchTerm=Apple"
Next make the URLRequest with the URL string, and set its http method.
var urlRequest = URLRequest(url: requestUrl)
urlRequest.httpMethod = "POST"
Then set the cookies for the URLRequest.
urlRequest.setValue("myPreferredClub=4969", forHTTPHeaderField: "Cookie")
urlRequest.httpShouldHandleCookies = true
Finally send the URLRequest with Alamofire, and use the response data in whatever way I wish.
Alamofire.request(urlRequest).responseString { response in
if let content: String = response.result.value {
// I do things with the content of the urlRequest...
}
}
I'm using the following code to test a behavior in NSURLCache. I initialize an API instance in AppDelegate. I configure the manager according to Alamofire's documentation, I configure the shared cache, and I assign dataTaskWillCacheResponse to make sure that the response will indeed be cached.
Then I call makeRequest which checks if a cached response exists (which it shouldn't on the first launch) and then I use my manager to make a request using the same URL so that the request is equivalent throughout the test.
My breakpoint at dataTaskWillCacheResponse is hit, I continue, the responseJSON block is executed and is Successful so I performTests using the request.
First, I check if the response is cached. It is: good!
Second, (and this is the problem) I remove the cached response for that request and then check if it exists. It does: bad!
Third, I check if removing all cached responses will remove that response. It does: good! But it's odd that that worked and the previous attempt at just removing the single response didn't...
Here's the code:
import Alamofire
class API: Manager.SessionDelegate {
var manager: Manager!
override init() {
super.init()
manager = Manager(session: urlSession(), delegate: self)
configureCache(memoryCapacityMB: 5, diskCapacityMB: 25)
manager.delegate.dataTaskWillCacheResponse = { urlSession, dataTask, cachedResponse in
// Placing a breakpoint here confirms that the response is going to be cached
return cachedResponse
}
}
private func urlSession() -> NSURLSession {
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
return NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
}
private func configureCache(memoryCapacityMB memory: Int, diskCapacityMB disk: Int) {
let memoryCapacity = memory * 1024 * 1024
let diskCapacity = disk * 1024 * 1024
let sharedCache = NSURLCache(memoryCapacity: memoryCapacity, diskCapacity: diskCapacity, diskPath: nil)
NSURLCache.setSharedURLCache(sharedCache)
}
// MARK: Request
func makeRequest() {
// The response should be nil on the first launch since nothing has been cached
let request = NSURLRequest(URL: NSURL(string: "http://jsonplaceholder.typicode.com/posts")!)
let response = NSURLCache.sharedURLCache().cachedResponseForRequest(request)
print(response)
manager.request(.GET, request.URLString).responseJSON { response in
switch response.result {
case .Success:
self.performTests(with: response.request!)
case .Failure:
break
}
}
}
func performTests(with request: NSURLRequest) {
// Should exist
var response = NSURLCache.sharedURLCache().cachedResponseForRequest(request)
print(response)
// And it does: good!
// Remove the cached resopnse and check if it exists
NSURLCache.sharedURLCache().removeCachedResponseForRequest(request)
response = NSURLCache.sharedURLCache().cachedResponseForRequest(request)
print(response)
// And it does: bad!
// Try removing all cached responses and check if it exists
NSURLCache.sharedURLCache().removeAllCachedResponses()
response = NSURLCache.sharedURLCache().cachedResponseForRequest(request)
print(response)
// And it doesn't: good! But odd...
}
}
So how does one remove the cached response of a single request then? And is this unintended behavior? Or is NSURLCache behaving correctly and I'm just missing something? Thanks ahead of time for taking a look!
My recollection is that most URL cache changes are not synchronous. They only actually happen after you return to the run loop and allow various asynchronous callbacks to occur.
Try running the rest of the code asynchronously after a delay of 3-5 seconds and see if the request has been removed.
If that doesn't fix the problem, file a bug.
I've created a script/api which is suppose to add a record to my database when running a specific url. However i'm not sure how to run this url. I do not expect anything back just to run this url? how can i do this?
var identifier = UIDevice.currentDevice().identifierForVendor.UUIDString
var addViewUrl = "http://url/addview.php?type=ios&identifier=\(identifier)&newsid=\(newsObject?.id)"
Based on my comment:
You should get a response and check for errors.
Also there is always the possibility to call a URL asynchronously to avoid blocking the GUI if the request takes a long time.
This can be made using delegate patterns or with completions handlers like in Objective-C.
Example:
var url = NSURL.URLWithString(addViewUrl)// Creating URL
var request = NSURLRequest(URL: url)// Creating Http Request
var queue: NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{(response:NSURLResponse!, responseData:NSData!, error: NSError!) -> Void in
if error != nil
{
println(error.description)
}
else
{
var responseStr:NSString = NSString(data:responseData, encoding:NSUTF8StringEncoding)
//Everything went fine
}
})