Swift UIImageView Firebase DispatchQueue - swift

I am using firebase to save and load my images. I have created a new view in Xcode and am using the same code I have been using to load profile images. Yet, this is now throwing an error saying that the url string is nil. The image url data disappears after "DispatchQueue.global().async". What could be causing this and how could I track this? Very strange how this code works for other views yet for this new view it is throwing an error.
let businessProfilePicture = dictionary["profPicString"] as! String
if businessProfilePicture.count > 0 {
let url = URL(string: businessProfilePicture)
print(url)
print("printing the url here to check")
DispatchQueue.global().async {
let dataURL = try? Data(contentsOf: url!)
print(dataURL)
print("printing the data url here")
DispatchQueue.main.async {
print(dataURL)
print("Printing Data to check")
let image = UIImage(data: dataURL!)?.potter_circleo
self.businessProfilePicture.contentMode = UIView.ContentMode.scaleAspectFill
self.businessProfilePicture.image = image
}
}
Full Code
func getWorkLocation() {
let uid = Auth.auth().currentUser?.uid
var profPicURL: String = ""
Database.database().reference().child("employees").child(uid!).child("Business").observe(.value, with: { snapshot in
if snapshot.exists() {
let dictionary = snapshot.value as? NSDictionary
self.businessName.text = dictionary?["businessName"] as? String
self.businessStreet.text = dictionary?["businessStreet"] as? String
self.businessCity.text = dictionary?["businessCity"] as? String
profPicURL = dictionary?["profPicString"] as! String
// set image
if profPicURL.count > 0 {
let url = URL(string: profPicURL)
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
DispatchQueue.main.async {
let image = UIImage(data: data!)?.potter_circle
self.businessProfilePicture.contentMode = UIView.ContentMode.scaleAspectFill
self.businessProfilePicture.image = image
}
}
} else {
let image = UIImage(named: "profile picture")?.potter_circle
self.businessProfilePicture.contentMode = UIView.ContentMode.scaleAspectFill
self.businessProfilePicture.image = image
}
} else {
self.businessName.text = ""
self.businessStreet.text = "Go to Add Work Location to send request"
self.businessCity.text = ""
self.deleteButton.isEnabled = false
}
})
}

Are you certain that the URL you create from profPicURL is being created properly?
URL(string:) can fail and return nil. If you then go on to implicitly unwrap it in Data(contentsOf: url!) you will crash.
Similarly, try? Data(contentsOf: url) can return nil. If it does, then when you implicitly unwrap it in UIImage(data: data!) you will crash.
As Jacob said in comments, you need to learn more about implicitly unwrapped optionals. To get you started, you might structure your code something like this:
if let url = URL(string: profPicURL) {
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url),
let image = UIImage(data: data)?.potter_circle
{
DispatchQueue.main.async {
self.businessProfilePicture.contentMode = UIView.ContentMode.scaleAspectFill
self.businessProfilePicture.image = image
}
} else {
// raise an an error or set self.businessProfilePicture.image to a generic image or something
}
}
} else {
// raise an an error or set self.businessProfilePicture.image to a generic image or something
}

Related

Retrieve data from Firebase Realtime Database to display I UIImage

I would like to know how to retrieve an image from a link in the Firebase Realtime.. I have set up my UIImageView in my app and linked it to my ViewController, how ever the code that I added doesn't give me error but it doesn't display anything.
My viewcontroller
let userID = Auth.auth().currentUser?.uid
Database.database().reference().child("Users").child(userID!).child("Total Progress").observeSingleEvent(of: .value, with: { snapshot in
if let url = snapshot.value as? String {
URLSession.shared.dataTask(with: URL(string: url)!) { [self] data, response, error in
if error == nil {
let image = UIImage(data: data!)
financialLiteracy.image = image
}
}.resume()
}
})
My Firebase Realtime Database
Realtime Database
let userID = Auth.auth().currentUser?.uid
Database.database().reference().child("Users").child(userID!).child("Total Progress").observeSingleEvent(of: .value, with: { snapshot in
if let url = snapshot.value as? String {
URLSession.shared.dataTask(with: URL(string: url)!) { [self] data, response, error in
if let data = data {
DispatchQueue.main.async {
let image = UIImage(data: data)
self.financialLiteracy.image = image
}
}
if let error = error {
print(error.localizedDescription)
}
}.resume()
}
})
use main thread for ui updates
the second way to get it
let userID = Auth.auth().currentUser?.uid
Database.database().reference().child("Users").child(userID!).child("Total Progress").observeSingleEvent(of: .value, with: { snapshot in
DispatchQueue.global(qos: .background).async {
if let urlstring = snapshot.value as? String,
let url = URL(string: urlstring) {
if let data = try? Data(contentsOf: url) {
DispatchQueue.main.async {
let image = UIImage(data: data)
self.financialLiteracy.image = image
}
}
}
}
}

swift soup parsing with evaluateJavaScript in webview

I want to get a video URL from HTML -:
<div class="_53mw" data-store="{"videoID":"607125377233758","playerFormat":"inline","playerOrigin":"permalink","external_log_id":null,"external_log_type":null,"rootID":607125377233758,"playerSuborigin":"misc","useOzLive":false,"playbackIsLiveStreaming":false,"canUseOffline":null,"playOnClick":true,"videoDebuggerEnabled":false,"videoViewabilityLoggingEnabled":false,"videoViewabilityLoggingPollingRate":-1,"videoScrollUseLowThrottleRate":true,"playInFullScreen":false,"type":"video","src":"*https:\/\/video.fdel10-1.fna.fbcdn.net\/v\/t42.1790-2\/271574467_153621553687266_1119427332980623121_n.mp4?_nc_cat=106&ccb=1-5&_nc_sid=985c63&efg=eyJ2ZW5jb2RlX3RhZyI6InN2ZV9zZCJ9&_nc_ohc=HuufVpgJRnEAX8_AEix&_nc_rml=0&_nc_ht=video.fdel10-1.fna&oh=00_AT8eUqTIMXRHidmafowZmL7-o4k2JG0FqA4QbFKNINiQ8Q&oe=61DD4DFB","width":414,"height":621*,"trackingNodes":"FH-R","downloadResources":null,"subtitlesSrc":null,"spherical":false,"sphericalParams":null,"defaultQuality":null,"availableQualities":null,"playStartSec":null,"playEndSec":null,"playMuted":null,"disableVideoControls":false,"loop":true,"numOfLoops":13,"shouldPlayInline":true,"dashManifest":null,"isAdsPreview":false,"iframeEmbedReferrer":null,"adClientToken":null,"audioOnlyVideoSrc":null,"audioOnlyEnabled":false,"permalinkShareID":null,"feedPosition":null,"chainDepth":null,"videoURL":"https:\/\/www.facebook.com\/100069898026392\/videos\/607125377233758\/","disableLogging":false}" data-sigil="inlineVideo">
i am doing this-:
do {
let doc: Document = try SwiftSoup.parseBodyFragment(html)
let headerTitle = try doc.title()
// my body
let body = doc.body()
// elements to remove, in this case images
let undesiredElements: Elements? = try body?.select("a")
//remove
try! undesiredElements?.remove()
// print("Header title: \(headerTitle)")
print("Header body: \(body)")
}
How to do this with swift soup
Try this code - :
do {
let doc: Document = try SwiftSoup.parse(html)
let size = try doc.getElementsByClass("_53mw").first()
let data = try size?.attr("data-store")
print(data!)
let videoUrl = convertToDictionary(text: data!)
let url = videoUrl!["src"] as! String
print(url)
} catch Exception.Error(let type, let message) {
print("Message: \(message)")
} catch {
print("error")
}

How to put image to NSCache in Swift?

I make some code using swift 4 to load image from URL, but every time I add images to server, it took a lot of time to load it in colection view or table view. I want to try store it in NScache but i dont understand to do it. can anyone help me, I'm new in swift :(
#objc func loadPosts() {
let url = URL(string: "http://someURL/Url.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let body = "phomepost=\(homepost)"
request.httpBody = body.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
if error == nil {
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
self.comments.removeAll(keepingCapacity: false)
self.images.removeAll(keepingCapacity: false)
self.collectionView?.reloadData()
guard let parseJSON = json else {
print("Error While Parsing")
return
}
guard let posts = parseJSON["posts"] as? [AnyObject] else {
print("Error while parseJSONing")
return
}
self.comments = posts.reversed()
for i in 0 ..< self.comments.count {
let path = self.comments[i]["path"] as? String
if !path!.isEmpty {
let url = NSURL(string: path!)!
let imageData = try? Data(contentsOf: url as URL)
let image = UIImage(data: imageData! as Data)!
self.images.append(image)
} else {
let image = UIImage()
self.images.append(image)
}
}
self.collectionView?.reloadData()
//print(posts.count)
} catch {
print(error)
}
}else{
print(error)
}
})
}.resume()
}
You can use something like this:
private let cache = NSCache<NSString, NSData>()
.....
func downloadImage(url: String, handler: #escaping(Data?, Error?) -> Void){
let cacheID = NSString(string: url)
if let cachedData = cache.object(forKey: cacheID) {
handler((cachedData as Data), nil)
}else{
if let url = URL(string: url) {
let session = URLSession(configuration: urlSessionConfig)
var request = URLRequest(url: url)
request.cachePolicy = .returnCacheDataElseLoad
request.httpMethod = "get"
session.dataTask(with: request) { (data, response, error) in
if let _data = data {
self.cache.setObject(_data as NSData, forKey: cacheID)
handler(_data, nil)
}else{
handler(nil, error)
}
}.resume()
} else {
// NetworkError is a custom error
handler(nil, NetworkError.invalidURL)
}
}
}
}
This will add a small animation while loading using image set.
let imageCache = NSCache<AnyObject, AnyObject>()
extension UIImageView {
func loadImageFromUrl(urlString: String) {
let loader1 = UIImage(named: "loaderImage1.png")
let loader2 = UIImage(named: "loaderImage2.png")
let loader3 = UIImage(named: "loaderImage3.png")
let imageArray = [loader1, loader2, loader3]
let animatedImage = UIImage.animatedImage(with: imageArray as! [UIImage], duration: 1.7)
if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage{
self.image = imageFromCache
return
} else {
self.image = animatedImage
Alamofire.request(urlString, method: .get).response { (responseData) in
if let data = responseData.data {
DispatchQueue.main.async {
if let imageToCache = UIImage(data: data){
imageCache.setObject(imageToCache, forKey: urlString as AnyObject)
self.image = imageToCache
}
}
}
} //alamofire
}
}
}

Using Cloudkit Assets as a UIimage

I have images saved in CloudKit as an asset. There are other attributes for each record as well. I can gather the record and use the other attributes, but I'm unable to use the asset in my ImageView. I'm new to Swift programming, therefore the error I receive does not make any sense.
let container = CKContainer.default()
let publicDB = container.publicCloudDatabase
let query1 = CKQuery(recordType: "movieArray", predicate: predicate2)
publicDB.perform(query1, inZoneWith: nil) {(results:[CKRecord]?, error:Error?) in
if error != nil {
DispatchQueue.main.async {
print("Cloud Query Error - Fetch Establishments: \(String(describing: error))")
}
return
}
for record in results! {
DispatchQueue.main.async {
let asset = record["myImageKey"] as? CKAsset
let data = NSData(contentsOf: (asset?.fileURL)!)
let image = UIImage(data: data! as Data)
print("Test")
self.detailImage.image = image
}
self.movieCast.text = record.object(forKey: "Actors") as? String
self.detailDescriptionLabel.text = record.object(forKey: "Description") as? String
let youLink = record.object(forKey: "youtubeTag") as! String
self.loadYoutube(videoID: youLink)
}
}
The error I get is on this line:
let data = NSData(contentsOf: (asset?.fileURL)!)
it says:
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1020527c4
I attempted to remove it from the main thread, but I receive the same error.
Maybe the problem is that the CKAsset record is nil, but you are forcing to have a fileURL value.
Try to obtain CloudKit image with this snippet
if let asset = record["myImageKey"] as? CKAsset,
let data = try? Data(contentsOf: (asset.fileURL)),
let image = UIImage(data: data)
{
self.detailImage.image = image
}

Converting Array of URLs into Array of UIImage

I have an array of URLs linking to image files, how do I store them into an array of UIImages?
var imagesArray = [UIImage]()
let links = ["http://example.com/image1.jpg","http://example.com/image2.jpg"] as [String]
There must be an easy solution.
If it was one image I could do something like the following:
let url = URL(string: link2image!)!
let imageData = try? Data(contentsOf: url)
let image = UIImage(data: imageData!)!
self.image.append(image)
The easiest solution would be to just iterate through the array and download the images synchronously using Data(contentsOf:), however, this would be quite insufficient due to synchronous execution.
let images = links.flatMap{ link->UIImage? in
guard let url = URL(string: link) else {return nil}
guard let imageData = try? Data(contentsOf: url) else {return nil}
return UIImage(data: imageData)
}
A better solution would be to download the images asynchronously in parallel, I have written a UIImage extension for this using PromiseKit:
extension UIImage {
class func downloadedAsync(fromUrl url: URL)->Promise<UIImage> {
return Promise{ fulfill, reject in
URLSession.shared.dataTask(with: url, completionHandler: { data, response, error in
guard let data = data, error == nil else {
reject(error!); return
}
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
reject(NSError(domain: "Wrong HTTP response code when downloading image asynchronously",code: (response as? HTTPURLResponse)?.statusCode ?? 1000));return
}
guard let mimeType = response?.mimeType, mimeType.hasPrefix("image"), let image = UIImage(data: data) else {
reject(NSError(domain: "No image in response", code: 700)); return
}
fulfill(image)
}).resume()
}
}
}
You call it for an array of links like this:
var imageDownloadTasks = links.flatMap{ link in
guard let url = URL(string: link) else {return nil}
return UIImage.downloadedAsync(fromUrl: url)
}
Then execute the promises:
when(fulfilled: imageDownloadTasks).then{ images in
//use the images, images is of type [UIImage]
}
Maybe you can try Functional programming use: map closure ...
links.map({
if let imageData = try? Data(contentsOf: URL(string: $0)!) {
UIImage(data: imageData)
}
})
But you must know if your links is wrong, it will crash. So, I suggest you make some guard to prevent these crashes. like this...
guard let url = URL(string: $0) else { return nil }