get NSURLSession download progress in all view controllers - nsurlsession

So i have a FirstViewController where i download a video with the progress view and progress is working fine using this code
func startDownloading() {
let download = Downloads(url: videoUrl!.absoluteString!)
download.downloadTask = self.downloadsSession.downloadTaskWithURL(videoUrl!)
download.downloadTask!.resume()
download.isDownloading = true
}
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
// 1
print("URLSession Completed for url \(downloadTask.originalRequest?.URL?.absoluteString)")
if let originalURL = downloadTask.originalRequest?.URL?.absoluteString,
destinationURL = localFilePathForUrl(originalURL) {
let fileManager = NSFileManager.defaultManager()
do {
try fileManager.removeItemAtURL(destinationURL)
} catch {
// Non-fatal: file probably doesn't exist
}
do {
try! fileManager.copyItemAtURL(location, toURL: destinationURL)
} catch let error as NSError {
print("Could not copy file to disk: \(error.localizedDescription)")
}
}
}
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
print("URLSession inProgress \(Float(totalBytesWritten)/Float(totalBytesExpectedToWrite))")
if let downloadUrl = downloadTask.originalRequest?.URL?.absoluteString,
let download = activeDownloads[downloadUrl] {
//THIS SETS THE PROGRESS
download.progress = Float(totalBytesWritten)/Float(totalBytesExpectedToWrite)
self.downloadView.state = .Downloading
self.downloadView.setProgress(Double(totalSize)!, animated: true)
}
}
now this code updates FirstViewControllers downloadView.progress correctly but what i want is when i go to SecondViewController i should get the progress of this ongoing download in SecondVC too without starting the download progress again (i know downloading again would be very dumb).

The best way is to separate your network request manager code from the view controller:
Create a separate class to manage the requests, and move your delegate code there.
In the didWriteData method, use NSNotificationCenter to broadcast the notification to any interested view class or make your first view controller notify the second one if it exists.
In each of your view controller classes, register for the notification and when you receive it, update the status accordingly.

Related

How do I read a text-file's content from it's shared Dropbox link in Swift 4.2 (without downloading)?

I'm having a hard time figuring out how to output a simple text-file's content from it's shared Dropbox link (without downloading) through Swift 4.2.
For Example:
let url = URL(string: "https://www.dropbox.com/s/rokwv82h54ogwy1/test.txt?dl=0")!
// the dropbox link above is a shared link so anyone can view it
do {
let content = try String(contentsOf: url)
print("File Content: \(content)")
} catch let error as NSError {
print("\(error)")
}
When I run this code I get this error:
Error Domain=NSCocoaErrorDomain Code=260 "The file “test.txt” couldn’t be opened because there is no such file."(there's more to the error but it's quite big)
Can anyone help me out please? Thanks.
There's more to the error but it's quite big
Do not strip error messages. If you don't know to fix this issue, you probably don't know what to strip to keep it valuable.
How to fix your problem
Select target
Switch to Signing & Capabilities tab
App Sandbox - Network - enable Outgoing Connections (Client)
Change the URL (dl=0) to (dl=1)
0 = display web page with a preview and download link
1 = do not display any web page, just serve the file
let url = URL(string: "https://www.dropbox.com/s/rokwv82h54ogwy1/test.txt?dl=1")!
// Change dl=0 to dl=1 ^
do {
let content = try String(contentsOf: url)
print("File Content: \(content)")
} catch let error as NSError {
print("\(error)")
}
Run again and you'll get:
File Content:
This is a test. If you can read this, you have passed! :)
Do not use String(contentsOf: url), because it's not async and it will block the main thread (UI).
Asynchronous example - imagine you have a view controller with one text field (label) and you'd like to display the file content there:
import Cocoa
class ViewController: NSViewController {
#IBOutlet var textField: NSTextField!
override func viewWillAppear() {
super.viewWillAppear()
textField.stringValue = "Loading ..."
loadRemoteFile()
}
func loadRemoteFile() {
let url = URL(string: "https://www.dropbox.com/s/rokwv82h54ogwy1/test.txt?dl=1")!
let task = URLSession.shared.dataTask(with: url) { data, _, error in
// Following code is not called on the main thread. If we'd like to
// modify UI elements, we have to dispatch our code on the main thread.
// Hence the DispatchQueue.main.async {}.
if let error = error {
print("Failed with error: \(error)")
DispatchQueue.main.async { self.textField.stringValue = "Failed" }
return
}
guard let data = data,
let content = String(data: data, encoding: .utf8) else {
print("Failed to decode data as an UTF-8 string")
DispatchQueue.main.async { self.textField.stringValue = "Failed" }
return
}
print("Content: \(content)")
DispatchQueue.main.async { self.textField.stringValue = content }
}
// At this point, we have a task which will download the file, but the task
// is not running. Every task is initially suspended.
task.resume() // Start the background task
// At this point, your program normally continues, because the download
// is executed in the background (not on the main thread).
}
}

implement declaration and uiapplication delegate

New to iOS development so I'm a little confused on this part of implement the point of sale sdk
Usage
Swift
Import Declaration: import SquarePointOfSaleSDK
// Replace with your app's URL scheme.
let yourCallbackURL = URL(string: "your-url-scheme://")!
// Your client ID is the same as your Square Application ID.
// Note: You only need to set your client ID once, before creating your first request.
SCCAPIRequest.setClientID("YOUR_CLIENT_ID")
do {
// Specify the amount of money to charge.
let money = try SCCMoney(amountCents: 100, currencyCode: "USD")
// Create the request.
let apiRequest =
try SCCAPIRequest(
callbackURL: yourCallbackURL,
amount: money,
userInfoString: nil,
merchantID: nil,
notes: "Coffee",
customerID: nil,
supportedTenderTypes: .all,
clearsDefaultFees: false,
returnAutomaticallyAfterPayment: false
)
// Open Point of Sale to complete the payment.
try SCCAPIConnection.perform(apiRequest)
} catch let error as NSError {
print(error.localizedDescription)
}
Finally, implement the UIApplication delegate method as follows:
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
guard let sourceApplication = options[.sourceApplication] as? String,
sourceApplication.hasPrefix("com.squareup.square") else {
return false
}
do {
let response = try SCCAPIResponse(responseURL: url)
if let error = response.error {
// Handle a failed request.
print(error.localizedDescription)
} else {
// Handle a successful request.
}
} catch let error as NSError {
// Handle unexpected errors.
print(error.localizedDescription)
}
return true
}
I'm a little confused where I put these respective pieces of code. My best guess is that the UIApplication delegate goes into AppDelegate.swift?
You can set the client ID in the appDidFinishLaunching method in AppDelegate since it only needs to be set once.
The code that creates and executes the API request can go in whatever view controller you'd like to use to initiate the payment.
You are correct, the last method should be added to your AppDelegate.

Simple Swift Cocoa app with WebKit: Upload picture doesn't work

I decided to make my own FB chat app that simply shows https://messenger.com on a WebView after trying other 'freemium' apps.
My ViewController.swift has just a few lines of code that loads URL on the web view
import Cocoa
import WebKit
class ViewController: NSViewController {
#IBOutlet weak var webView: WebView!
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "https://messenger.com")
let request = NSURLRequest(URL: url!);
webView.mainFrame.loadRequest(request);
}
override var representedObject: AnyObject? {
didSet {
// do nothing
}
}
}
Besides adding NSAppTransportSecurity key to info.plist to unblock HTTP traffic via HTTPS connection, I have not done any other settings.
Question
Please take a look at this image first.
Everything looks fine & working except two things.
Uploading image does not work - I labeled as 1 in the picture.
normally (as in other released apps or from web browsers) if you click that icon, it shows an explorer to upload a picture like below.
My app completely ignores user's click on that icon so I cannot upload any pictures to the chat. Interestingly, if I drag and drops the picture to the webview, it uploads fine.
Shared picture does not show up - I labeled as 2 in the picture.
again, from other browsers or released apps, it shows the pictures that I shared with participants like below. (of course I censored the pictures)
my app tries to load the pics, but does not display them. I can see it trying to load because I see circular progress indicator while loading.
Why?
I suspect that there might be a way to listen to the JavaScript that's triggered within the WebView and link to a file explorer or something like that?
This I have no idea. I'm logged into Messenger (basically Facebook), so I think session is not a problem here. Maybe some jQuery loading issue??
What should I do to solve these issues?
There is indeed a delegate method to open a new panel called runOpenPanelForFileButtonWithResultListener, documentation here.
In the delegate method, just create a new NSOpenPanel like this:
func webView(sender: WebView!, runOpenPanelForFileButtonWithResultListener resultListener: WebOpenPanelResultListener!, allowMultipleFiles: Bool) {
let openDialog = NSOpenPanel()
if (openDialog.runModal() == NSOKButton) {
let fileName: String = (openDialog.URL?.path)!
resultListener.chooseFilename(fileName) // Use chooseFilenames for multiple files
}
}
I just tried to create a WebView from Messagers App and images are loading well.
You should try to enable WebView options like "Autoload Images" or "Enable Animated Images" from interface builder (or by code).
This code works for me and what is nice here is that you make the download of image asynchronous. You can find more about this concept here: http://www.raywenderlich.com/79149/grand-central-dispatch-tutorial-swift-part-1 and: http://www.raywenderlich.com/79150/grand-central-dispatch-tutorial-swift-part-2
Edited
I. You have to create new Class or a new iOS Swift File named ImageLoader with this content:
class ImageLoader {
var cache = NSCache()
class var sharedLoader : ImageLoader {
struct Static {
static let instance : ImageLoader = ImageLoader()
}
return Static.instance
}
func imageForUrl(urlString: String, completionHandler:(image: UIImage?, url: String) -> ()) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {()in
var data: NSData? = self.cache.objectForKey(urlString) as? NSData
if let goodData = data {
let image = UIImage(data: goodData)
dispatch_async(dispatch_get_main_queue(), {() in
completionHandler(image: image, url: urlString)
})
return
}
var downloadTask: NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: urlString)!, completionHandler: {(data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
if (error != nil) {
completionHandler(image: nil, url: urlString)
return
}
if data != nil {
let image = UIImage(data: data)
self.cache.setObject(data, forKey: urlString)
dispatch_async(dispatch_get_main_queue(), {() in
completionHandler(image: image, url: urlString)
})
return
}
})
downloadTask.resume()
})
}
}
II. In your actual viewController you call the method 'imageForUrl' from ImageLoaded following this lines of code:
ImageLoader.sharedLoader.imageForUrl("http://upload.wikimedia.org/wikipedia/en/4/43/Apple_Swift_Logo.png", completionHandler:{(image: UIImage?, url: String) in
self.myImage.image = image!
})
I took the code from this link: https://teamtreehouse.com/community/does-anyone-know-how-to-show-an-image-from-url-with-swift
Edited for image loaded on webview
Here is the code. It works perfect for me:
override func viewDidLoad() {
super.viewDidLoad()
let myWebView:UIWebView = UIWebView(frame: CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height))
myWebView.loadRequest(NSURLRequest(URL: NSURL(string: "https://scontent-vie1-1.xx.fbcdn.net/hphotos-xap1/v/t1.0-9/12144725_10204647881668565_4367944825116750386_n.jpg?oh=5ecdae91f5258ffe0e0355e176f8eb8a&oe=56B007CA")!))
self.view.addSubview(myWebView)
}

Send email In-game using sprite kit in xcode 7 beta3?

I am making an iPad game in sprite kit using swift in xcode 7beta3 and I want the results of the game to be send to the users email after the game is completed. The user should press a button called send and redirect to where they can type in their email-address and send the message. But I have no idea how to make and send an email.
I have been searching all around the internet for an answer to this question, but all are older version answers. I hope you can help.
Thanks in advance
EDIT:
I have been searching some more and i found a solution (here: http://kellyegan.net/sending-files-using-swift/), but I still have a problem. In my GameViewController i have added:
override func viewDidLoad() {
super.viewDidLoad()
let scene = StartGameScene(size: view.bounds.size)
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .ResizeFill
skView.presentScene(scene)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
internal func sendEmail() {
//Check to see the device can send email.
if( MFMailComposeViewController.canSendMail() ) {
print("Can send email.")
let mailComposer = MFMailComposeViewController()
mailComposer.mailComposeDelegate = self
//Set the subject and message of the email
mailComposer.setSubject("Have you heard a swift?")
mailComposer.setMessageBody("This is what they sound like.", isHTML: false)
if let filePath = NSBundle.mainBundle().pathForResource("Math", ofType: "txt") {
print("File path loaded.")
if let fileData = NSData(contentsOfFile: filePath) {
print("File data loaded.")
mailComposer.addAttachmentData(fileData, mimeType: "text/plain", fileName: "Math")
}
}
self.presentViewController(mailComposer, animated: true, completion: nil)
}
}
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
self.dismissViewControllerAnimated(true, completion: nil)
}
The sendMail() is called in one of my gameScenes when you press a button.
The problem is that I get an error when I press that button. It prints out
Can send email.
File path loaded.
File data loaded.
as it should, but then it gives an error:
Could not cast value of type 'UIView' (0x1964ea508) to 'SKView' (0x19624f560).
I think the problem is the self.presentViewController(), but I have no idea how to fix it.

Swift: Error updating UI component when using a delegate

I'm trying to update a progress bar with the progress of loading a load of values into CoreData. However, whenever I try to call an update on my progressView component, I get a fatal error stating that "unexpectedly found nil while unwrapping an Optional value".
The interesting thing is that this happens even if I put 'self.progressView.progress = 0.5' in the delegate method of my program - indicating that it's the progressView component it can't find rather than an issue with the value. A quick check with println also confirms the value does exist and so isn't nil. Note that if I put the 'self.progressView.progress = 0.5' statement under a function connected directly to a button, it works fine so it must be some sort of issue with the command being called from the delegate.
Can anyone work out what I'm doing wrong here? Thanks for your help.
Delegate method:
class ViewControllerUpdate: UIViewController, NSURLSessionDelegate, NSURLSessionDownloadDelegate, saveUpdate {
[....]
func updateStatus(status: String, progress: Float?) {
if let percentProgress = progress? {
self.progressView.progress = 0.5
}
//println(progress) - NOTE THIS IS CORRECTLY POPULATED WITH THE APPROPRIATE VALUE
}
Calling class:
protocol saveUpdate {
func updateStatus(status:String, progress:Float?)
}
class sqlPullSave {
let classtoUpdate: saveUpdate = ViewControllerUpdate()
func saveTSVtoSQL(fromFile: NSURL) -> Int {
//Load up the information into a Dictionary (tsv)
//let tsvURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(fromFileName, ofType: fromFileExtension)!)
let tsvURL: NSURL = fromFile
let tab = NSCharacterSet(charactersInString: "\t")
let tsv = CSV(contentsOfURL: tsvURL, separator: tab)
//let defResult: AnyObject = tsv.rows[0]["Name"]!
//let tryagain:String = AnyObjecttoString(tsv.rows[1]["Name"]!)
//load the data into the SQLite database...
dispatch_async(dispatch_get_main_queue()) {
for a in 0..<tsv.rows.count {
self.SQLsaveLine(self.AnyObjecttoString(tsv.rows[a]["Name"]!),
name_l: "",
desc: self.AnyObjecttoString(tsv.rows[a]["1"]!),
jobTitle: self.AnyObjecttoString(tsv.rows[a]["2"]!),
extn: self.AnyObjecttoString(tsv.rows[a]["3"]!)
// update status
var percentComplete: Float = (Float(a) / Float(tsv.rows.count))
self.classtoUpdate.self.updateStatus("SQLload", progress: percentComplete)
}
}
return 0
}

Resources