I want to download an image from Facebook and store it to my cache, which I call over HTTPS. If I just use HTTP everything works fine, but if I change it to HTTPS it does not work anymore.
Here is my code:
// Grab the artworkUrl key to get an image URL for thumbnail
var urlString: NSString = rowData["cover"] as NSString
// Check our image cache for the existing key. This is just a dictionary of UIImages
var image: UIImage? = self.imageCache.valueForKey(urlString) as? UIImage
if( !image? ) {
// If the image does not exist, we need to download it
var imgURL: NSURL = NSURL(string: urlString)
// Download an NSData representation of the image at the URL
var request: NSURLRequest = NSURLRequest(URL: imgURL)
var urlConnection: NSURLConnection = NSURLConnection(request: request, delegate: self)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!,data: NSData!,error: NSError!) -> Void in
if !error? {
//var imgData: NSData = NSData(contentsOfURL: imgURL)
image = UIImage(data: data)
// Store the image in to our cache
self.imageCache.setValue(image, forKey: urlString)
cell.image = image
println(self.imageCache)
}
else {
println("Error: \(error.localizedDescription)")
}
})
}
else {
cell.image = image
}
})
The URL I want to use is this:
https://fbcdn-sphotos-d-a.akamaihd.net/hphotos-ak-xpf1/t1.0-9/q71/s720x720/995054_485489274919674_7207866955460529362_n.jpg
The Error is a "Timeout".
With this URL everything works fine:
http://fbcdn-sphotos-d-a.akamaihd.net/hphotos-ak-xpf1/t1.0-9/q71/s720x720/995054_485489274919674_7207866955460529362_n.jpg
Thanks,
Tobias
From what I can tell NSURLConnection sometimes runs into trouble with HTTPS connections. Try adding these two methods to your class (and mark it as NSURLConnectionDelegate) (from this answer):
func connection(connection: NSURLConnection!, canAuthenticateAgainstProtectionSpace protectionSpace: NSURLProtectionSpace!) -> Bool {
return true
}
func connection(connection: NSURLConnection!, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge!) {
challenge.sender.useCredential(NSURLCredential(forTrust: challenge.protectionSpace.serverTrust), forAuthenticationChallenge: challenge)
}
Related
I want to load images into connectionDidFinishLoading method and the method func connection(_connection: NSURLConnection, didReceive: Data) is not getting called
class ImageDownload: UIImageView,NSURLConnectionDelegate,NSURLConnectionDataDelegate
{
var imageSaved:UIImage!
var imageDownloaded:UIImage!
var connection2:NSURLConnection = NSURLConnection()
var data: NSMutableData = NSMutableData()
var urlstring:String = ""
var fileURL:URL!
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray
var pathPlist:String!
func downloadImage()
{
let imgdownload :String = "http://image.tmdb.org/t/p/w500" + urlstring
// let urlnew: NSURL = NSURL(string: imgdownload)!
//print(urlnew,"url")
let url: NSURL = NSURL(string: imgdownload)!
let request1: NSMutableURLRequest = NSMutableURLRequest(url: url as URL)
// let request2: NSMutableURLRequest = NSMutableURLRequest(url: urlnew as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 60.0)
connection2 = NSURLConnection(request: request1 as URLRequest, delegate: self, startImmediately: false)!
connection2.start()
}
func setURL(url:String) -> Void
{
print(url,"url")
urlstring = url
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
fileURL = documentsDirectory.appendingPathComponent(url)
print(fileURL,"fileurl")
if FileManager.default.fileExists(atPath: fileURL.path)
{
let image = UIImage(contentsOfFile: fileURL.path)
print("file exists")
self.image = image
}
else
{
downloadImage()
//let imgdownload :String = "http://image.tmdb.org/t/p/w500" + url
// let request = URL(string: imgdownload)
// let myUrl = NSURL(string: imgdownload)
//print("image loaded")
// self.image = self.imageSaved
}
}
func connection(_ connection: NSURLConnection, didReceive response: URLResponse)
{
print("in didReceive response\n")
self.data = NSMutableData()
}
func connection(_connection: NSURLConnection, didReceive: Data)
{
print("in didReceive data\n")
self.data.append(data as Data)
print(data,"image data is")
}
func connection(_ connection: NSURLConnection, didFailWithError error: Error)
{
print("connection error = \(error)")
}
func connectionDidFinishLoading(_ connection: NSURLConnection)
{
}
}*
I'm not familiar with the Swift syntax for NSURLConnection, so there's a nonzero chance I'm missing something subtle with the delegate method names, but off the top of my head, I see a couple of bigger problems:
Why are you using NSURLConnection in Swift? Every version of iOS that supports Swift also has NSURLSession. Just use the shared session, and your behavior will be almost identical to NSURLConnection.
You're using HTTP URLs. In all recent versions of iOS, you have to add special bits to your Info.plist if you want your app to be able to access HTTP URLs. I do not recommend doing that. Just use HTTPS.
You can get free TLS certs for HTTPS from Let's Encrypt. My guess is that as soon as you set that up and switch to an HTTPS URL, your problems will go away even with the existing code. But you should still be using NSURLSession.
I'm trying to download video preview base on remote video URL. In my project, the server cannot return snapshot image of the videos, that's why I have to do it manually.
In table view, I have code like this in cellForItemAt to get video preview
DataManager.sharedInstance.getCachedImage(url: movie.url!, handler: { (image) in
cell.ivCover.image = image
})
and my getCachedImage function in DataManager:
func getCachedImage(url: String, handler: #escaping(_ result:UIImage) -> Void){
DispatchQueue.global().async {
let userDefaults = UserDefaults.standard
if let imageData = userDefaults.object(forKey: url){
if let finalImg = UIImage(data: imageData as! Data){
DispatchQueue.main.async {
handler(finalImg)
}
print("USING CACHED IMG")
}else{
DispatchQueue.main.async {
handler(#imageLiteral(resourceName: "cover"))
}
print("Cannot parse cached data to image. Use default")
}
}else{
let asset = AVURLAsset(url: URL(string: url)!)
let generate = AVAssetImageGenerator(asset: asset)
generate.appliesPreferredTrackTransform = true
var thumbTime = asset.duration
thumbTime.value = 1
var imgRef:CGImage?
do{
print("Downloading thum from url: \(url)")
imgRef = try generate.copyCGImage(at: thumbTime, actualTime: nil)
}catch let error{
print("Error download thum: \(error.localizedDescription)")
}
var finalImg:UIImage
if let _ = imgRef{
finalImg = UIImage(cgImage: imgRef!)
userDefaults.set(UIImagePNGRepresentation(finalImg), forKey: url)
}else{
finalImg = #imageLiteral(resourceName: "cover")
print("Download thumnail failed. Use default")
}
DispatchQueue.main.async {
handler(finalImg)
}
}
}
}
The problem is that, sometimes I scroll the Collection view, UI is freezed, sometimes it's not. Please note that this video is on REMOTE SERVER, NOT local video.
I've spent days to figure out the issue but still not able to find out what went wrong. Please help!
Or is there any existing library I can use?
import UIKit
import Photos
class ImportViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func myButton(_ sender: Any) {
let videoURL = "https://youerdomin.com/file.mp4"
print("1")
DispatchQueue.global(qos: .background).async {
print("2")
if let url = URL(string: videoURL), let urlData = NSData(contentsOf: url) {
print("3")
let documentsPath =
NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0];
let filePath="\(documentsPath)/tempFile.mp4"
DispatchQueue.main.async {
print("4")
urlData.write(toFile: filePath, atomically: true)
PHPhotoLibrary.shared().performChanges({
print("5")
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: filePath))
}) { completed, error in
print("6")
if completed {
print("Video is saved!")
}else{
print("7: ",filePath)
}
}
}
}else{
print("8")
}
}
}
}
I am able to display image from the sources, but was wondering how to display image from the URL link:
ccell.avatarImageView.image= [UIImage imageNamed:#"home_new.png"];
Thanks for the help.
Add this outside of the class scope:
import UIKit
extension UIImageView {
func loadurl(url: URL) {
DispatchQueue.global().async { [weak self] in
if let data = try? Data(contentsOf: url) {
if let image = UIImage(data: data) {
DispatchQueue.main.async {
self?.image = image
}
}
}
}
}
}
Then add this to wherever you're trying to load your image:
let urlYourURL = URL (string: #"home_new.png")
avatarImageView.loadurl(url: urlYourURL!)
Download the image as data from URL using NSURLConnection and use below code
let imageView:UIImageView = UIImageView.init(image: UIImage.init(data: **data**, scale: 1.0));
I am trying to read/display an image from Firebase. I am first encoding the image and then posting this encoded String to Firebase. This runs fine. When I try and decode the encoded string from Firebase and convert it to an image, I am getting a nil value exception.
This is how I am saving the image to Firebase
var base64String: NSString!
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
self.dismissViewControllerAnimated(true, completion: nil)
imageToPost.image = image
var uploadImage = image as! UIImage
var imageData = UIImagePNGRepresentation(uploadImage)!
self.base64String = imageData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
let ref = Firebase(url: "https://XXX.firebaseio.com")
var quoteString = ["string": self.base64String]
var usersRef = ref.childByAppendingPath("goalImages")
var users = ["image": quoteString]
usersRef.setValue(users)
displayAlert("Image Posted", message: "Your image has been successfully posted!")
}
This is how I am trying to read the image from Firebase
// ViewController.swift
import UIKit
import Firebase
class ViewController: UIViewController {
#IBOutlet weak var image: UIImageView!
var base64String: NSString!
#IBAction func buttonClicked(sender: AnyObject) {
sender.setTitle("\(sender.tag)", forState: UIControlState.Normal)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let ref = Firebase(url: "https://XXX.firebaseio.com/goalImages/image/string")
ref.observeEventType(.Value, withBlock: { snapshot in
self.base64String = snapshot.value as! NSString
let decodedData = NSData(base64EncodedString: self.base64String as String, options: NSDataBase64DecodingOptions())
//Next line is giving the error
var decodedImage = UIImage(data: decodedData!)
self.image.image = decodedImage
}, withCancelBlock: { error in
print(error.description)
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The error says: "fatal error: unexpectedly found nil while unwrapping an Optional value"; decodedData is nil. Could someone explain what is going wrong.
Instead of
let decodedData = NSData(base64EncodedString: self.base64String as String,
options: NSDataBase64DecodingOptions())
try adding IgnoreUnknownCharacters
NSDataBase64DecodingOptions.IgnoreUnknownCharacters
Use Example: Encode a jpg, store and read from firebase
encode and write our favorite starship
if let image = NSImage(named:"Enterprise.jpeg") {
let imageData = image.TIFFRepresentation
let base64String = imageData!.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
let imageRef = myRootRef.childByAppendingPath("image_path")
imageRef.setValue(base64String)
read and decode
imageRef.observeEventType(.Value, withBlock: { snapshot in
let base64EncodedString = snapshot.value
let imageData = NSData(base64EncodedString: base64EncodedString as! String,
options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
let decodedImage = NSImage(data:imageData!)
self.myImageView.image = decodedImage
}, withCancelBlock: { error in
print(error.description)
})
EDIT 2019_05_17
Update to Swift 5 and Firebase 6
func writeImage() {
if let image = NSImage(named:"Enterprise.jpg") {
let imageData = image.tiffRepresentation
if let base64String = imageData?.base64EncodedString() {
let imageRef = self.ref.child("image_path")
imageRef.setValue(base64String)
}
}
}
func readImage() {
let imageRef = self.ref.child("image_path")
imageRef.observeSingleEvent(of: .value, with: { snapshot in
let base64EncodedString = snapshot.value as! String
let imageData = Data(base64Encoded: base64EncodedString, options: Data.Base64DecodingOptions.ignoreUnknownCharacters)!
let decodedImage = NSImage(data: imageData)
self.myImageView.image = decodedImage
})
}
Firebase Engineer here:
I highly recommend using the new Firebase Storage API for uploading images to Firebase. It's simple to use, low cost, and backed by Google Cloud Storage for huge scale.
You can upload from NSData or an NSURL pointing to a local file (I'll show NSData, but the principle is the same):
// Data in memory
let data: NSData = ...
// Create a reference to the file you want to upload
let riversRef = storageRef.child("images/rivers.jpg")
// Upload the file to the path "images/rivers.jpg"
let uploadTask = riversRef.putData(data, metadata: nil) { metadata, error in
if (error != nil) {
// Uh-oh, an error occurred!
} else {
// Metadata contains file metadata such as size, content-type, and download URL.
let downloadURL = metadata!.downloadURL
// This can be stored in the Firebase Realtime Database
// It can also be used by image loading libraries like SDWebImage
}
}
You can even pause and resume uploads, and you can easily monitor uploads for progress:
// Upload data
let uploadTask = storageRef.putData(...)
// Add a progress observer to an upload task
uploadTask.observeStatus(.Progress) { snapshot in
// Upload reported progress
if let progress = snapshot.progress {
let percentComplete = 100.0 * Double(progress.completedUnitCount) / Double(progress.totalUnitCount)
}
}
I had the following method in a separate class:
class API: NSObject {
var data = NSData()
var delegate: APIProtocol?
func getItems(callback: (Array<Image>) -> ()) {
let urlPath: NSString = "http://localhost:3000/files"
let url = NSURL(string: urlPath)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Accept")
let config = NSURLSessionConfiguration.defaultSessionConfiguration() as NSURLSessionConfiguration
let session = NSURLSession(configuration: config) as NSURLSession
var dataTask = NSURLSessionDataTask()
dataTask = session.dataTaskWithRequest(request) { (data, response, error) in
if (error == nil) {
println("API at URL \(url)")
let responseArray = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: nil) as NSArray
var images = Image[]()
for item: AnyObject in responseArray {
var location = Image(dict: item as NSDictionary)
images.append(location)
}
var img = images[0] as Image
callback(images)
//self.delegate?.didReceiveResponse(responseArray)
}
}
dataTask.resume()
}
}
I couldn't get my tableView to reload when calling self.tableView.reloadData() inside the callback() until I added the dispatch_async() around it.
My questions are:
1) Why wouldn't it work without it and is it the proper thing for me to do now that it's refreshing the tableView correctly?
2) Is there another way to get it working without having to add the dispatch on the main queue?
api.getItems() { (theArray) in
dispatch_async(dispatch_get_main_queue(), {
self.images = theArray
self.tableView.reloadData()
if (viaPullToRefresh) {
self.refreshControl.endRefreshing()
}
})
}
When creating a NSURLSession you can specify the delegate/completion queue. If you don't specify a queue
the session creates a serial operation queue for performing all
delegate method calls and completion handler calls.
So this means that your callbacks are called on a private serial queue. Now, all UI must be updated on the main queue, this is why tableView.reloadData() wasn't working.
To remove the dispatch call to the main_queue create the session like this
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: nil, delegateQueue: NSOperationQueue.mainQueue())