Swift clearing JSON cache? - ios

I'm working with some API data that gets updated frequently.
I recently discovered that the data does not update properly on the phone, when it's updated on the server.
After hours on hours trying to troubleshoot this, I finally simply tried deleting the app from my phone, and reinstalling in. And it worked.
After some further testing I discovered that it's printing out old JSON.
Once I delete the app and reinstall it, it successfully prints out the correct updated JSON.
From that I gathered that it's probably an issue with the phone caching the old JSON data somehow.
So how can I go about clearing this cache in swift? or forcing it to make a new request.
(I'm using swiftyJson, although I don't think that has anything to do with this specific problem)
I did find one other question like this, but it's old (2014 in Obj-C) and there was no answers.
Here's how I get my data:
var request = NSURLRequest(URL: formulaAPI!)
var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: nil, error: nil)
var formula = JSON(data: data!)
// Loop through the api data.
for (index: String, portfolio: JSON) in formula["portfolio"] {
// Save the data into temporary variables
tempStockName = portfolio["name"].stringValue
tempTicker = portfolio["ticker"].stringValue
tempPurchasePrice = portfolio["purchase_price"].floatValue.roundTo(2)
tempWeight = portfolio["percentage_weight"].floatValue
latestAPIPrice = portfolio["latest_price"].floatValue.roundTo(2)
tempDaysHeld = portfolio["days_owned"].intValue
// Continues on for quite a while, but the data in the above segment is definitely getting filled with old JSON data, so the issue is arising before this point
}
I tried changing my request to the following:
var request = init(formulaAPI: NSURL, cachePolicy: NSURLRequestCachePolicy, timeoutInterval: NSTimeInterval)
But that causes an error: "Use of local variable 'request' before it's declaration"
Any help figuring this out would be greatly appreciated!

Instead of creating your request with,
NSURLRequest(URL: formulaAPI!)
you should use the following method so that you can explicitly set the cache policy.
var request = NSURLRequest(URL: formulaAPI!, cachePolicy: .ReloadIgnoringLocalCacheData, timeoutInterval: 30)
NSURLRequest(URL:) uses the default came policy, NSURLRequestUseProtocolCachePolicy, and a time out interval of 60 seconds.

Related

Reload data of a json file after being updated

after hours of search I need your help. I have setup a program for iOS showing recipes of my private cooking book. The recipes are stored in a json file on my web server. I use another application on my MAC to update the recipes in this json file and to store it on my web server. Unfortunately the updated recipes are not shown in my iOS-application. The iOS-app shows always the old data - I guess they are stored locally on my iPhone/iPad after the first installation of the app?
How can I enforce that at every launch of the iOS-app on my iPhone/iPad the actual json data from the web server are used and not the old one.
I retrieve the data via this function I call from the viewDidLoad() and before the reloadData() of the UITableView showing the titles of the recipes
func initKochbuch() {
let url = URL(string: selektiertesKochbuch)!
let urlSession = URLSession.shared
let task = urlSession.dataTask(with: url) { (data, response, error) in
guard let data = data else {
debugPrint("Fehler beim Laden", error ?? "Unbekannter Fehler")
return
}
self.kochbuch_local.rezepte = try! JSONDecoder().decode([Rezept].self, from: data)
self.initRezeptAnsicht()
OperationQueue.main.addOperation {self.tableView.reloadData()}
}
task.resume()
}
What do I have to do in addition? Thanks for your support.
Few possibilities:
hope you are not storing the data locally(coredata/plist/any file), and loading it anywhere in your app.
Your web server might be caching the old data, please check once.
See if you have any hardcoded sample data which was used for testing purpose but not removed after actual implementation.
Hope this helps.

Swift (Xcode) REST API Connection using OAuth 1

Quick background, I am extremely new in this realm. I am aware that this type of question has been asked before and answered successfully. The issue that I am experiencing is caused by the inability to wrap my head around the process of establishing the connection. I have spent hours (into days) searching for the answer and I am still unsuccessful. This has become my "white whale" so to speak.
I am using Xcode 9 with Swift version 4. Many of the answer I come across use Objective-C and I cannot mix and match. So I would like to UNDERSTAND why I am unable to connect and the correct process to connect so I can write the code with the understanding of what I am doing. Lastly, I have signed up (and completed) a few paid Udemy courses to try and learn the process correctly. I have been able to connect to API sources but OAuth 1 is tripping me up. Any constructive help would be incredibly appreciated.
Background:
I am attempting to connect to the Fat Secret database. I would like to connect a search bar to the food.search functionality and also the food.get for another search bar.
Company- FatSecret
URL for API- platform.fatsecret.com/rest/server.api
URL to FatSecret documentation (I have gone through this MANY times)- http:// {space} platform.fatsecret. {space }com/api/Default. {space} aspx?screen=rapiauth
Parameters- Parameters {
oauth_consumer_key - consumer_key (I have a consumer key)
oauth_signature_method - "HMAC-SHA1"
oauth_timestamp - The date and time, expressed in the number of seconds since January 1, 1970 00:00:00 GMT. The timestamp value must be a positive integer and must be equal or greater than the timestamp used in previous requests
oauth_nonce - A randomly generated string for a request that can be combined with the timestamp to produce a unique value
oauth_version - Must be "1.0"
}
As I previously stated, the answer to my question is displayed above. I understand that part but I do not understand how to incorporate it into my code.
Past code-
let url = URL(string: "I am unable to post more than 2 links due to my rep so I put {space} in the above url to circumvent the error. I used the listed url from the parameters")!
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
if error != nil { print("success")
} task.resume()
The above code is what I used to establish the connection. I receive "success" in the console so I expanded my parameters.
let url = URL(string: "I am unable to post more than 2 links due to my rep so I put {space} in the above url to circumvent the error. I used the listed url from the parameters")!
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
if error != nil { print(error)
if let urlContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(jsonResult)
} catch {
} task.resume()
The above code produces nothing in the console. I believe (sorry for my ignorance) that the reason I am not getting a response is because I am not sending any authorization in the request, nor am I am sending in the correct encoding. I imagine that I can create the parameters by var/let statements and then call on those statements but I am not able to see the way to do that. I could likely also store all of my connection information in a different swift file or class and call on that when I need to access data. This base signature is required with every request. I have to imagine that best practice would be setting it up that way but again, I can't visualization the process. It becomes a trial and error process that results in incredible frustration.
Again, any help would be incredibly appreciated. I apologize for the length of this post. Thank you for taking the time to read this post.
This may be late but I have successfully managed to implement the FatSecret REST API and have created a small Xcode project that shows how I handled OAuth. The only calls that can be made are food.search and food.get. https://github.com/NicholasBellucci/FatSecretSwift

How to solve issue with getting Data using URL Session?

I am using URL Session to get Data from a web API and load them in a tableView.
Every thing is working fine except that whenever I get the data the first time, the data is always returned the same even if it was changed in the backend side.
The data is only returned new when I delete the application and install it again.
Any idea on how to solve this please?
If you are saying that when deleting the application your problem is solved then the issue is in caching.
To solve this either add:
URLCache.shared.removeAllCachedResponses()
or:
let config = URLSessionConfiguration.default
config.requestCachePolicy = .reloadIgnoringLocalCacheData
let session: URLSession = URLSession(configuration: config)
This will solve your problem and ignore caching so that the data will be returned new everytime.

NSURLSession Performance - Probable race conditions or blocked threads?

I've ran into a bit of a performance issue with my iOS app, this is my first time working with NSURLSession and NSURLRequest, and although I've tried to inform myself as much as I can, I've hit a wall trying to debug a performance issue I'm facing.
So here's what I got: I've got an iOS 9 app written in Swift 2, I'm communicating with a NodeJS/Express server through Get, Post and Put Http requests, utilizing NSURLRequest and NSURLMutableRequest. I'm sending requests to fetch a group of objects (All together no more than 12000 bytes), however the requests are taking a significant amount of time (sometimes up to a minute). I've added logging to the nodeJs server and I can see that the requests take no longer than 30 milliseconds to be processed.
Note: I'm unsure if this is relevant, but I'm using a singleton "helper "class to make all my api requests and parse the results (saving authentication tokens, parsing JSON objects and saving them to Core Data, saving user preferences to NSUserDefaults, etc), I'm using a singleton so i can access it statically and I'm parsing all the data without saving anything in singleton's properties other than the URL of the server and the NSURLSession.
Here's what my code looks like.
//On initialization of the helper class
private let session = NSURLSession.sharedSession()
func getAllObjects() {
let route = "api/someRoute"
let request = getRequest(route)
request.timeoutInterval = httpTimeout
session.dataTaskWithRequest(request, completionHandler: ResultingObjects).resume()
}
The getRequest method returns a formatted NSMutableURLRequest, shown
here:
func getRequest(route: String) -> NSMutableURLRequest {
let request = NSMutableURLRequest()
request.URL = NSURL(string: "\(serverUrl)/\(route)")!
request.HTTPMethod = "GET"
request.addValue("Bearer \(self.AuthenticationToken()!)", forHTTPHeaderField: "Authorization")
return request
}
The completion handler will parse the objects returned and notify
the main thread with the resulting parsed objects, as so:
private func ResultingObjects(data: NSData?, response: NSURLResponse?, error: NSError?) {
if let d = data {
if !isAuthorized(d){
return
}
do {
if let JSON = try NSJSONSerialization.JSONObjectWithData(d, options: []) as? NSDictionary {
if let message = JSON["message"] as? String {
if message == "Empty result" {
//- Return notification to be handled in main thread
notifyMainThread(NoObjectsFetched, payload: nil)
return
}
}
if let objcts = JSON["SomeObjects"] as? NSArray {
if let SomeObjects = parseResultingObjects(objcts) {
//- Return notification to be handled in main thread
notifyMainThread(ObjectsFetched, payload: ["payload": SomeObjects])
}
return
}
}
}
catch {
print("Error getting resulting objects")
}
}
else if let e = error {
print("\(e), could not process GET request")
}
}
I've also tried parsing the resulting objects on the main thread but
that doesn't seem to make a difference.
If you are curious, this is how I'm sending data to the main thread:
private func notifyMainThread(notification: String, payload: AnyObject?) {
dispatch_async(dispatch_get_main_queue(), {
if let p = payload {
NSNotificationCenter.defaultCenter().postNotificationName(notification, object: nil,
userInfo: p as! [String: [MYMODEL]])
}
else {
NSNotificationCenter.defaultCenter().postNotificationName(notification, object: nil)
}
});
}
What I've found out:
Nothing makes sense! I've attempted debugging this but I cant really pin point what the issue is, When the debugger hits my "getAllObjects" method, it can take a good few seconds (up to 45 seconds) before the server logs that it received and processed the request (which it usually takes around 30 milliseconds). As far as I can tell, this happens for all requests types. Also, once the application gets the data back (super fast), it takes a long time (around 4 seconds) to parse it, and its only around 11kbs.
I've also attempted to change the requests cache policy in case the application was checking the validity of the cached records with the server, I used ReloadIgnoringLocalAndRemoteCachedData which didn't work either.
Now, this sounds like a memory leak
If I pause the application at any point (after using it for a few minutes), I can see a concerning number of threads. I'm honestly not too familiar with IOS so I'm unsure if this threads are from the simulator or if they all belong to the app, the app streams video contents (which has no latency issues) usin the AVPlayer class and I believe that many of this threads are related to this, however I'm unsure if this is normal, here's a screenshot of what I mean (Note the scroll bar T_T) Screenshot
Could it be that I've got a memory leak or some zombie threads considerably slowing the performance of my app? The only really noticeable delay happens only on HTTP requests which is quite odd, no other part of my UI lags, and no other feature in my app suffers from performance issues (even streaming video contents from a url).
What would be the best way to profile this performance issues to pin point the source of the problem?
UPDATE 1:
Thanks to the suggestions by Scriptable, I've managed to address the threading issue (caused by multiple AVPlayers doing their thing). The performance of the requests however are not solved.
Its worth pointing out, the server is physically in the same country as where I'm making the requests from, when making requests from the browser or form the Command Line the requests are almost immediate.
Also, when I randomly pause the app (while I wait to the request to happen) I can see a 'mach_msg_trap' in some of the threads, I'm not familiar with this but I believe this might be a race condition? or a deadlock?

POST Queries in Swift for given website

I am trying to make queries to get the fuel type and consumption of a specified car (the user enters both make and model) for an iOS app written in Swift.
The app is targeted for Spain, and I have found a website that allows the user to enter make and model, and it returns the details for that car (http://coches.idae.es/portal/BaseDatos/MarcaModelo.aspx). I have seen using the tool WireShark, that the query is based on POST instead of GET. But I am not quite sure how I can make the requests within the app I am developing, or how to handle the info that is sent to me back from the sender.
Is there any way to make those requests to the given website? If so, I would really appreciate some help on the subject, I am new in iOS development and am looking forward to learning as much as possible.
Thanks :)
Many people prefer to use AFNetworking for making HTTP requests. However you don't need to do that. You said that its a POST request. Setting that up is easy even without AFNetworking using NSMutableURLRequest. I'm assuming you have a link to the API and not just to the aspx page. My Spanish is pretty weak so I can't look up the API reference for you but here's how you can make the request and receive data from the server. You will have to put the correct values and parse the responses:
let request = NSMutableURLRequest(URL: NSURL(string: "/* Paste URL here */")!)
request.HTTPMethod = "POST"
// Do this as many times are required for filling in the headers.
request.addValue("/* The value for the HTTP header */", forHTTPHeaderField: "/*The header field like Accept-Type, etc..*/")
// If you need an HTTP body too then make the JSONObj as a dictionary or array or whatever and then
let data = NSJSONSerialization.dataWithJSONObject(JSONObj, options: [])
request.HTTPBody = data // This needs to be NSData.
// Now make the request.
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request, { (data, response, error) -> Void in
if error == nil
{
assert(data != nil)
let JSON = NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [NSObject: AnyObject]
// If you are using swift 2 this needs to be in a do try catch statement.
// TODO: Use JSON for whatever.
}
else
{
print(error!.localizedDescription)
}
}
task?.resume()
Let me know if you have any other questions or if the API doesn't use JSON or is completely different.

Resources