URLSession cached data - ios

I'm new in iOS dev and I do not understand one think. So I have gz file and inside gzip there is xml file. I need to download gz file, every time user start the app. First time when I start my app I use this code to get data. Problem is my xml file was offline on server for few day and my app always start without problem and show data with no problem. So all my files was cached on device?. I want my data is retrieved every time while the user start the application. I am not sure do I did something wrong? Thanks
let url = NSURL(string: "http://sitename/xxx.gz")
if url != nil {
let task = URLSession.shared.dataTask(with: url! as URL, completionHandler: { (data, response, error) -> Void in
if error == nil {
let nsdata = data as NSData?
let content = nsdata?.gunzipped()
let dataContent = content as Data?
let urlContent = NSString(data: dataContent!, encoding: String.Encoding.ascii.rawValue) as NSString!
let xml = XMLParser()
xml.getDataforTable(data: urlContent as! String)
NotificationCenter.default.post(Notification(name: Notification.Name(rawValue: "XmlDataLoaded"), object: nil))
} else {
NotificationCenter.default.post(Notification(name: Notification.Name(rawValue: "DataNotLoaded"), object: nil))
}
})
task.resume()
}

enter code here
in AppDelegate.swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
call your web services method inside this method
}

Related

TikTok LoginKit iOS Integration Issue

I am implementing social login with TikTok in my app, From official documentation I implemented Basic setup and connected with my AppDelegate https://developers.tiktok.com/doc/getting-started-ios-quickstart-swift. Implemented loginkit with there sample code but request.send completionBlock is not getting any response or do not enter into completion block after we authorised from TikTok app. Please help if any one has implemented tiktok login kit in iOS.
/* STEP 1 */
let scopes = "user.info.basic,video.list" // list your scopes
let scopesSet = NSOrderedSet(array:scopes)
let request = TikTokOpenSDKAuthRequest()
request.permissions = scopesSet
/* STEP 2 */
request.send(self, completion: { resp -> Void in
/* STEP 3 */
if resp.errCode == 0 {
/* STEP 3.a */
let clientKey = ... // you will receive this once you register in the Developer Portal
let responseCode = resp.code
// replace this baseURLstring with your own wrapper API
let baseURlString = "https://open-api.tiktok.com/demoapp/callback/?code=\(responseCode)&client_key=\(clientKey)"
let url = NSURL(string: baseURlstring)
/* STEP 3.b */
let session = URLSession(configuration: .default)
let urlRequest = NSMutableURLRequest(url: url! as URL)
let task = session.dataTask(with: urlRequest as URLRequest) { (data, response, error) -> Void in
/* STEP 3.c */
}
task.resume()
} else {
// handle error
}
}
Thanks to author's comment I figured that out too. In my case, there was no SceneDelegate in the project, so I had 3 url-related methods implemented in AppDelegate as per TikTok's documentation:
1:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool
2:
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any)
3:
func application(_ application: UIApplication, handleOpen url: URL) -> Bool
The docs also suggested that 1st method should use a default value of [:] for options, which is plainly wrong so I removed it.
I also had Firebase dynamic links implemented in the 1st method:
if let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url) {
self.handleDynamicLink(dynamicLink)
return true
}
Turns out, if you remove the 1st method completely and move Firebase DL handling to method #2 everything starts working! Dynamic links are handled and TT's completion block finally gets called

application openURL giving invalid urls

The function
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
is still called like normal, but when I do:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
do {
let contentsOfFile = try NSString(contentsOfFile: url.path, encoding: String.Encoding.utf8.rawValue)
Swift.print("COF \(contentsOfFile)")
} catch let error {
Swift.print("error \(error)")
}
...
}
I get the error "The file “____” couldn’t be opened because there is no such file."
This used to work in iOS 12. I'm not doing anything with SceneDelegate or anything, so I'm not sure why it's giving me invalid URLs now.
Update: if I drag a file from my Dropbox onto the iOS Simulator, it works. If I drag a file from anywhere else on my computer, it doesn't work.
The function open url can be used to read the files like below ,
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let stringPath = Bundle.main.path(forResource: "bbc", ofType: "json") // File name
let fileUrl = URL(fileURLWithPath: stringPath!)
let canOpenBool = application(UIApplication.shared, open: fileUrl)
print(canOpenBool)
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
do {
let contentsOfFile = try NSString(contentsOfFile: url.path, encoding: String.Encoding.utf8.rawValue)
Swift.print("COF \(contentsOfFile)")
return true
} catch let error {
Swift.print("error \(error)")
return false
}
}
}
Well, I guess it's just something to do with the simulator. If I drag a file from my Dropbox onto the iOS Simulator, it works. If I drag a file from anywhere else on my computer onto the simulator, it doesn't work. But it still works fine on a real device, as far as I can tell.

How to set the default SyncConfiguration for Realm, so I can get it in multiple ViewControlllers without redundant code?

According to the:
Proper Realm usage patterns/best practices
What is the best practice or design pattern to maintain sync activity across multiple views
Design Pattern for Realm Database Swift 3.1 - Singleton
my approach is like:
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
DispatchQueue.main.async {
let username = "test#test.com"
let password = "Test123"
let serverUrl = URL(string: "http://test.com:9080")
let realmUrl = URL(string: "realm://test.com:9080/~/realmtest")
if let user = SyncUser.current {
Realm.Configuration.defaultConfiguration.syncConfiguration = SyncConfiguration(user: user, realmURL: realmUrl!)
} else {
SyncUser.logIn(with: .usernamePassword(username: username, password: password, register: false), server: serverUrl!, onCompletion: { (user, error) in
guard let user = user else {
print("Error: \(String(describing: error?.localizedDescription))")
return
}
Realm.Configuration.defaultConfiguration.syncConfiguration = SyncConfiguration(user: user, realmURL: realmUrl!)
})
}
}
return true
}
ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
print("SyncConfiguration: \(String(describing: Realm.Configuration.defaultConfiguration.syncConfiguration))")
self.realm = try! Realm()
}
When I open app for the first time nothing happens but when I open app the second time, Realm works fine.
Whenever I open app, the printed SyncConfiguration is nil. No errors!
Searched here and there and can't find an answer...
The problem is that you are using an async method to configure your Realm, but you don't call the print inside the completion handler of your method. You should only present your viewcontoller once your asynchronous call has finished.

How to Call Background Fetch Completion Handler Properly

Currently, I am using an API (PowerAPI) in which the "authenticate" function is called and then once the user's data has been processed, a notification is sent out. This authenticate function needs to be called as a background fetch. My question is whether my current way of calling the completion handler is even calling the completion handler and if there is a better way?
Currently this is in my app delegate class:
let api = PowerAPI.sharedInstance
var completionHandler: ((UIBackgroundFetchResult) -> Void)? = nil
func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
print("BG fetch start")
NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleTranscript:", name:"transcript_parsed", object: nil)
self.completionHandler = completionHandler
api.authenticate("server.com", username: "User", password: "password", fetchTranscript: true)
}
func handleTranscript(notification : NSNotification){
print("BG fetch finit")
completionHandler!(UIBackgroundFetchResult.NewData)
print(api.studentInformation)
}
The API is a singleton type object.
EDIT: The PowerAPI object is a class I wrote to download student data from a server and parse it. The "transcript_parsed" notification is a notification generated from within the PowerAPI directly after the "transcript_fetched" notification is sent out in the following asynchronous code (Also within PowerAPI):
let task = session.dataTaskWithRequest(request) {
(let data, let response, let error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
switch notificationID {
case "authentication_finished":
//NSString(data: data!, encoding: NSUTF8StringEncoding)! //used to return data from authentication
let success = self.parse(data!) //notification object is true if success
NSNotificationCenter.defaultCenter().postNotificationName(notificationID, object: success)
case "transcript_fetched":
NSNotificationCenter.defaultCenter().postNotificationName(notificationID, object: data)
default:
break
}
}

No Data in Array after appending using Swift and xcode

I am trying to download images from a url and then save them in an array of NSData.
I have a Class called Data Manager in which all my data is stored as well as functions for downloading images and getting data from URL.
In the same class I declare a variable called imageData of type [NSData] and let it equal an empty array as follows:
var imageData: [NSData] = []
here is what my other 2 functions look like:
func getDataFromUrl(url:NSURL, completion: ((data: NSData?, response: NSURLResponse?, error: NSError? ) -> Void)) {
NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
completion(data: data, response: response, error: error)
}.resume()
}
func downloadImage(url: NSURL){
print("Download Started")
print("lastPathComponent: " + (url.lastPathComponent ?? ""))
getDataFromUrl(url) { (data, response, error) in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
guard let data = data where error == nil else { return }
print(response?.suggestedFilename ?? "")
print("Download Finished")
self.imageData.append(data)
print("you have \(self.imageData.count)")
}
print("you still do have \(self.imageData.count)")
}
}
I call these functions in my app Delegate class under the function didFinishLaunchingWithOptions as so
let dataManager = DataManager()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
dataManager.URLStringArray.removeAll()
for url in dataManager.objects.imageURLS {
dataManager.URLStringArray.append(url)
}
for url in dataManager.URLStringArray {
dataManager.downloadImage(NSURL(string: url)!)
print(url)
}
return true
}
In my view controller I go to get the data in the image array via following function:
func returnImageData() -> [NSData] {
print("your image count is \(imageData.count))")
return imageData
}
but the array is empty! Even though through the whole process I noticed that the array was becoming larger and larger because the print to the logs were showing the array increasing!
Thanks!
Since you are using async call to download the image data, at the the time you are printing the count of the imageData the image is not yet downloaded and so the array is not yet populated. Of course this is assuming that you are using the right property names as Eric.D has pointed out.

Resources