can't download user profile image from FIRStorage - swift

when the user going to user profile tab he gets error and crash in the app and I gets error in the Xcode in the URLSession.shared.dataTask
func setupProfile(){
if Auth.auth().currentUser?.uid == nil{
logout()
}else{
usrnNameButton.isHidden = false
let uid = Auth.auth().currentUser?.uid
databaseRef.child("Users").child(uid!).observeSingleEvent(of: .value,
with: { snapshot in
if let dict = snapshot.value as? [String: AnyObject]
{
if let profileImageuRL = dict["pic"] as? String
{
let url = URL(string: profileImageuRL)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
self.PP.image = UIImage(data: data!)
}
}).resume()
}
if let coverImageuRL = dict["CoverPic"] as? String
{
let url = URL(string: coverImageuRL )
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
self.coverPic.image = UIImage(data: data!)
}
}).resume()
}
}
})
}
}
and this is my code to setup the user data
like that any one can help me
thank you

Xcode 8 • Swift 3
use this to download the image if you want
func downloadImage(url: URL) {
print("Download Started")
getDataFromUrl(url: url) { data, response, error in
guard let data = data, error == nil else { return }
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
DispatchQueue.main.async() {
self.imageView.image = UIImage(data: data)
}
}
}
Usage:
if let url = URL(string: "http://www.apple.com/euro/ios/ios8/a/generic/images/og.png") {
downloadImage(url: url)
}

hello thank you for replay now we fixed the error and the app still workin when we press in the profile tab but the profile image give me no a empty image {func setupProfile(){
if Auth.auth().currentUser?.uid == nil{
logout()
}else{
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
//userName.text = changeRequest?.displayName
usrnNameButton.isHidden = false
let uid = Auth.auth().currentUser?.uid
databaseRef.child("Users").child(uid!).observeSingleEvent(of: .value,
with: { snapshot in
if let dict = snapshot.value as? [String: AnyObject]
{
if let profileImageuRL = dict["pic"] as? String
{
//let url = URL(string: profileImageuRL)
//self.downloadImage(url: url!)
//self.PP.image = UIImage(data: data!)
//let resource = ImageResource(downloadURL: profileImageuRL, cacheKey: profileImageuRL)
self.PP.kf.setImage(with: profileImageuRL as? Resource)
}
if let coverImageuRL = dict["CoverPic"] as? String
{
self.coverPic.kf.setImage(with: coverImageuRL as? Resource)
}
}
})
}
}
}
the profile tab image you can see it [https://i.stack.imgur.com/g4ENe.jpg][1]
thank you again

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
}
}
}
}
}

Core data how to use NSMangedObjectContext in multithreaded

Okay, I've been going at this for a day and can't seem to figure out what I am doing wrong. This is how my data model looks like for core data.
This is how my code looks like.
class Service {
static let shared = Service()
private let numberOfPokemons = 151
func downloadPokemonsFromServer(completion: #escaping ()->()) {
let urlString = "https://pokeapi.co/api/v2/pokemon?limit=\(numberOfPokemons)"
guard let url = URL(string: urlString) else { return }
var id: Int16 = 0
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let err = error {
print("Unable to fetch pokemon", err)
}
guard let data = data else { return }
let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateContext.parent = CoreDataManager.shared.persistentContainer.viewContext
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let pokemonJSON = try decoder.decode(PokemonsJSON.self, from: data)
pokemonJSON.pokemons.forEach { (JSONPokemon) in
id += 1
let pokemon = Pokemon(context: privateContext)
pokemon.name = JSONPokemon.name
pokemon.url = JSONPokemon.detailUrl
pokemon.id = id
}
try? privateContext.save()
try? privateContext.parent?.save()
completion()
} catch let err {
print("Unable to decode PokemonJSON. Error: ",err)
completion()
}
}.resume()
}
private var detailTracker = 0
func fetchMoreDetails(objectID: NSManagedObjectID) {
guard let pokemon = CoreDataManager.shared.persistentContainer.viewContext.object(with: objectID) as? Pokemon, let urlString = pokemon.url else { return }
print(pokemon.name)
print()
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let err = error {
print("Unable to get more details for pokemon", err)
}
guard let data = data else { return }
let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateContext.parent = CoreDataManager.shared.persistentContainer.viewContext
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let pokemonDetailJSON = try decoder.decode(PokemonDetailJSON.self, from: data)
pokemonDetailJSON.types.forEach { (nestedType) in
let type = Type(context: privateContext)
type.name = nestedType.type.name
type.addToPokemons(pokemon)
}
try? privateContext.save()
try? privateContext.parent?.save()
} catch let err {
print("Unable to decode pokemon more details", err)
}
}.resume()
}
private var imageTracker = 0
func getPokemonImage(objectID: NSManagedObjectID) {
guard let pokemon = CoreDataManager.shared.persistentContainer.viewContext.object(with: objectID) as? Pokemon else { return }
let id = String(format: "%03d", pokemon.id)
let urlString = "https://assets.pokemon.com/assets/cms2/img/pokedex/full/\(id).png"
print(urlString)
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let err = error {
print("Unable to load image from session.", err)
}
guard let data = data else { return }
let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateContext.parent = CoreDataManager.shared.persistentContainer.viewContext
pokemon.image = data
self.imageTracker += 1
if self.imageTracker == self.numberOfPokemons {
try? privateContext.save()
try? privateContext.parent?.save()
}
}.resume()
}
}
I have 3 entities, which are Pokemon, Type & Ability. I am not doing nothing with ability right now, so we can just ignore that. The first func downloadPokemonFromServer just grabs the first 151 pokemon, saves the name and a url of pokemon. I then use that url to go into another URLSession and grab more information about that pokemon. Which is what the fetchMoreDetails func does. However, this func crashes my app. I don't know what I am doing wrong here, it crashes when I try to save it.
The third func getPokemonImage I go into another URLSession, get the data and save it to my pokemon image attribute. The thing is this works perfectly fine. It saves to my CoreData and it doesn't crash my app.
This is how I call it in my ViewController.
#objc func handleRefresh() {
if pokemonController.fetchedObjects?.count == 0 {
Service.shared.downloadPokemonsFromServer {
let pokemons = self.pokemonController.fetchedObjects
pokemons?.forEach({ (pokemon) in
Service.shared.getPokemonImage(objectID: pokemon.objectID)
//If I uncomment the line below it will crash my app.
//Service.shared.fetchMoreDetails(objectID: pokemon.objectID)
})
}
}
tableView.refreshControl?.endRefreshing()
}
Will someone pls help me figure out what I am doing wrong. Would really appreciate the help.
You need to make sure you're doing all the Core Data work on the same thread as the private context you've created. To do so please use:
privateContext.perform {
//Core data work: create new entities, connections, delete, edit and more...
}
This can prevent you a lot of headaches and troubles down the road
I think the problem is that you are trying to set a relationship between two objects from different contexts. Your pokemon object is registered with the view context:
guard let pokemon = CoreDataManager.shared.persistentContainer.viewContext.object(with: objectID) as? Pokemon, let urlString = pokemon.url else { return }
whereas your type object is registered with the private context:
let type = Type(context: privateContext)
type.name = nestedType.type.name
so this line will not work:
type.addToPokemons(pokemon)
I would try amending the code to use only the privateContext, something like this:
func fetchMoreDetails(objectID: NSManagedObjectID) {
let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateContext.parent = CoreDataManager.shared.persistentContainer.viewContext
guard let pokemon = privateContext.object(with: objectID) as? Pokemon, let urlString = pokemon.url else { return }
print(pokemon.name)
print()
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let err = error {
print("Unable to get more details for pokemon", err)
}
guard let data = data else { return }
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let pokemonDetailJSON = try decoder.decode(PokemonDetailJSON.self, from: data)
pokemonDetailJSON.types.forEach { (nestedType) in
let type = Type(context: privateContext)
type.name = nestedType.type.name
type.addToPokemons(pokemon)
}
try? privateContext.save()
try? privateContext.parent?.save()
} catch let err {
print("Unable to decode pokemon more details", err)
}
}.resume()
}

How to load images dynamically inside UITextView

I have a UITextView and appending TextAttachments inside of it. I am getting photos from web url. Here is the code:
image.load(url: URL(string: ("http://www.furkan.webofisin.com/upload/media/5db1b96366cd8.jpg")))
imageAttachment.image = image.image
let image1String = NSAttributedString(attachment: imageAttachment)
fullString.append(image1String)
And this is the load function as extension:
var imageUrlString: String?
func load(urlString: String) {
imageUrlString = urlString
guard let url = URL(string: urlString) else {
DispatchQueue.main.async {
let urlMessage = "Hata"
appDelegate.errorView(message: urlMessage, color: smoothGreenColor)
}
return
}
image = nil
if let imageFromCache = imageCache.object(forKey: urlString as NSString) {
self.image = imageFromCache
return
}
URLSession.shared.dataTask(with: url) { (data , response, error) in
if error != nil {
DispatchQueue.main.async {
let urlMessage = "Bir hata meydana geldi."
appDelegate.errorView(message: urlMessage, color: smoothGreenColor)
}
return
}
DispatchQueue.main.async {
let imageToCache = UIImage(data: data!)
if self.imageUrlString == urlString {
self.image = imageToCache
}
imageCache.setObject(imageToCache ?? UIImage(named: "astast")!, forKey: urlString as NSString)
}
}.resume()
}
Images not displaying at first time after refresh page its displaying because of caching.
I checked image.image as UIImage but its always nil before loading finished.
Here is the screenshot of first opening.
At first opening image data is nil
How can I detect and reload images when image data filled.
Add a completion as call to get image initially is asynchronous unlike from local cache
func load(urlString: String,completion:#escaping(() -> () )) {
imageUrlString = urlString
guard let url = URL(string: urlString) else {
DispatchQueue.main.async {
let urlMessage = "Hata"
appDelegate.errorView(message: urlMessage, color: smoothGreenColor)
}
return
}
image = nil
if let imageFromCache = imageCache.object(forKey: urlString as NSString) {
self.image = imageFromCache
completion()
return
}
URLSession.shared.dataTask(with: url) { (data , response, error) in
if error != nil {
DispatchQueue.main.async {
let urlMessage = "Bir hata meydana geldi."
appDelegate.errorView(message: urlMessage, color: smoothGreenColor)
}
return
}
DispatchQueue.main.async {
let imageToCache = UIImage(data: data!)
if self.imageUrlString == urlString {
self.image = imageToCache
}
imageCache.setObject(imageToCache ?? UIImage(named: "astast")!, forKey: urlString as NSString)
completion()
}
}.resume()
}
Call
image.load(url: URL(string: ("http://www.furkan.webofisin.com/upload/media/5db1b96366cd8.jpg"))) {
imageAttachment.image = image.image
let image1String = NSAttributedString(attachment: imageAttachment)
fullString.append(image1String)
}

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
}
}
}

Firebase Snapshot working correctly to retrieve user info but not being able to retrieve Profile Picture - Swift

I am getting a weird issue with my snapshot where the username and email are being fetched but my snapshot returns nil for a profile picture.
func displayUserInformation() {
let uid = Auth.auth().currentUser?.uid
Database.database().reference().child("users").child(uid!).observeSingleEvent(of: .value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
self.username.text! = (dictionary["username"] as? String)!
self.email.text! = (dictionary["email"] as? String)!
let profileImageUrl = (dictionary["photoUrl"] as? String)!
let url = URL(string: profileImageUrl)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
self.profilePicture.image? = UIImage(data: data!)!
print(data!)
}
}).resume()
print(profileImageUrl)
print(snapshot)
}
}, withCancel: nil)
}
The snapshot is working and the profileUrl DOES show the data so I don't know what might be the problem. Here's what the print(profileImageUrl) shows and the snapshot
https://firebasestorage.googleapis.com/v0/b/gastet-4cfd2.appspot.com/o/profileImages%2F031F0B9F-AA41-4C16-8446-CD07893F2CA7.jpg?alt=media&token=c0e07615-8166-40e7-8d92-22bb8fbc8c8e
Snap (wL40cuYrGAVNdCVvCGUhKsD64RH2) {
email = "ximeft29#gmail.com";
photoUrl = "https://firebasestorage.googleapis.com/v0/b/gastet-4cfd2.appspot.com/o/profileImages%2F031F0B9F-AA41-4C16-8446-CD07893F2CA7.jpg?alt=media&token=c0e07615-8166-40e7-8d92-22bb8fbc8c8e";
username = ximeft29;
}
I've duplicated your example in a sample project and have successfully been able to get it to asynchronously fetch the picture from the URL.
I've also rewritten the function to handle unwrapping the data a bit more gracefully. Any questions feel free to ask!
func displayUserInformation() {
if let currentUser = Auth.auth().currentUser {
Database.database().reference().child("users").child(currentUser.uid).observeSingleEvent(of: .value) { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject],
let username = dictionary["username"] as? String,
let email = dictionary["email"] as? String,
let urlString = dictionary["photoUrl"] as? String,
let url = URL(string: urlString) {
self.username.text = username // Set the username
self.email.text = email // Set the email
/*
* Fetch the image asyncronously
*/
URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
if let error = error {
print("There was an error fetching the image from the url: \n", error)
}
if let data = data, let profilePicture = UIImage(data: data) {
DispatchQueue.main.async() {
self.profilePicture.image = profilePicture // Set the profile picture
}
} else {
print("Something is wrong with the image data")
}
}).resume()
}
}
}
}