This is my netWorkOperations classs
import UIKit
class NetworkOpertions: NSObject {
private var actors = [Actor]()
func getMethod(OnCompletion:#escaping (Any)-> Void) {
guard let url = URL(string: "http://microblogging.wingnity.com/JSONParsingTutorial/jsonActors")else {return}
let session = URLSession.shared.dataTask(with:url){
(data,response,error) in
if let data = data {
print("This is Data:", data)
do{
let decoder = JSONDecoder()
let downloadedActors = try decoder.decode(Actors.self, from: data)
let res = data
}
OnCompletion(res)
}
catch let err{
print(err.localizedDescription)
// OnCompletion()
}
}
}
session.resume()
}
}
This is my ViewController class
import UIKit
class ViewController: UIViewController, UITableViewDataSource,UITableViewDelegate,UIPopoverPresentationControllerDel egate{
private var actors = [Actor]()
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Welcome"
tableView.delegate = self
tableView.dataSource = self
downloadJson()
tableView.tableFooterView = UIView()
}
func downloadJson() {
let netWork = NetworkOpertions()
let reponseValue = netWork.getMethod(){ (fetchValue)-> Void in
Here its throwing error:Invalid conversion from throwing function of type '(_) throws -> Void' to non-throwing function type '(Any) -> Void'
if fetchValue != nil {
print("MY VAlue:",fetchValue)
let decoder = JSONDecoder()
let downloadedActors = try decoder.decode(Actors.self, from: data)
self.actors = downloadedActors.actors
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return actors.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ActorCell") as? ActorCell else { return UITableViewCell() }
cell.nameLbl.text = actors[indexPath.row].name
cell.DOBLbl.text = actors[indexPath.row].dob
cell.countryCell.text = actors[indexPath.row].country
if let imageURL = URL(string: actors[indexPath.row].image) {
DispatchQueue.global().async {
let data = try? Data(contentsOf: imageURL)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
cell.imgView.image = image
}
}
}
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 70
}
}
please help me how to solve this error:
Invalid conversion from throwing function of type '(_) throws -> Void'
to non-throwing function type '(Any) -> Void'
The reason of the error is the missing do catch block wrapping the decode line
do {
let downloadedActors = try decoder.decode(Actors.self, from: data)
self.actors = downloadedActors.actors
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch { print(error) }
Related
I'm working with CocktailDB.
By creating a request I get a JSON file, parse it with Decodable protocol. From JSON I get all drinks' categories and display them as the sections of my tableview.
In each tableview section I want to display drinks from specific category (section's header). One drink per section cell from the category (drink's strDrink (name) and strDrinkThumb (image)).
I have a method that creates a request to get drinks from specific category - getDrinksFrom(category: String).
Please advice how can I call this method for specific section to get and display drinks from specific category in this section?
My code:
class ViewController: UIViewController {
var drinks = [Drink]()
var categories = [Category]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
getCategories()
getDrinksFrom(category: "Cocoa")
}
func getCategories() {
let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/list.php?c=list")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error == nil {
do {
self.categories = try JSONDecoder().decode(Categories.self, from: data!).drinks
DispatchQueue.main.async {
self.tableView.reloadData()
}
print(self.categories)
} catch {
print(error)
}
}
}.resume()
}
func getDrinksFrom(category: String) {
let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/filter.php?c=\(category)")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error == nil {
do {
self.drinks = try JSONDecoder().decode(Drinks.self, from: data!).drinks
DispatchQueue.main.async {
self.tableView.reloadData()
}
print(self.drinks)
} catch {
print(error)
}
}
}.resume()
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return categories.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return categories[section].strCategory
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "drinkCell") as! DrinkCell
cell.drinkName.text = drinks[indexPath.row].strDrink
let url = drinks[indexPath.row].strDrinkThumb
cell.drinkImage.downloaded(from: url)
return cell
}
}
// to download an image from web
extension UIImageView {
func downloaded(from url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
contentMode = mode
URLSession.shared.dataTask(with: url) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() { [weak self] in
self?.image = image
}
}.resume()
}
func downloaded(from link: String, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
downloaded(from: url, contentMode: mode)
}
}
Category Model:
struct Categories:Decodable {
var drinks: [Category]
}
struct Category:Decodable {
var strCategory: String
}
Drink Model:
struct Drinks:Decodable {
var drinks: [Drink]
}
struct Drink:Decodable {
var strDrink: String
var strDrinkThumb: String
}
What I have for know:
JSON structure:
My suggestion is to create a custom struct Category with name and drinks for the sections. It does not conform to Decodable, this is intended
struct Category {
let name : String
var drinks : [Drink]
}
and an appropriate data source array
var categories = [Category]()
then load and parse the categories with traditional JSONSerialization and populate the array by mapping the names. Further add a completion handler
func getCategories(completion: #escaping () -> Void) {
let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/list.php?c=list")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if let error = error { print(error); return }
do {
let result = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
let categoryNames = result["drinks"] as! [[String:String]]
self.categories = categoryNames.map{ Category(name: $0["strCategory"]!, drinks:[])}
completion()
} catch {
print(error)
}
}.resume()
}
To avoid naming confusion (too many drinks) name the root struct Response
struct Response : Decodable {
let drinks: [Drink]
}
Load the data related to a category and assign the drinks array to the corresponding array in categories
func getDrinksFrom(category: String) {
let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/filter.php?c=\(category)")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if let error = error { print(error); return }
do {
let drinks = try JSONDecoder().decode(Response.self, from: data!).drinks
guard let index = categories.firstIndex(where: {$0.name == category}) else { return }
self.categories[index].drinks = drinks
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print(error)
}
}.resume()
}
and replace viewDidLoad with
override func viewDidLoad() {
super.viewDidLoad()
getCategories { [weak self] in
self?.getDrinksFrom(category: "Cocoa")
}
}
Finally change the table view data source methods to match the section structure
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return categories.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return categories[section].name
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return categories[section].drinks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "drinkCell") as! DrinkCell
let category = categories[indexPath.section]
let drink = category.drinks[indexPath.row]
cell.drinkName.text = drink.strDrink
let url = drink.strDrinkThumb
cell.drinkImage.downloaded(from: url)
return cell
}
}
You can also put both functions together and load all drinks for all categories
func loadAllCategories() {
let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/list.php?c=list")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if let error = error { print(error); return }
do {
let result = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
let categoryNames = (result["drinks"] as! [[String:String]]).map{$0["strCategory"]!}
let group = DispatchGroup()
for category in categoryNames {
let categoryURLString = "https://www.thecocktaildb.com/api/json/v1/1/filter.php?c=\(category)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let categoryURL = URL(string: categoryURLString)!
group.enter()
let categoryTask = URLSession.shared.dataTask(with: categoryURL) { (categoryData, _, categoryError) in
defer { group.leave() }
if let categoryError = categoryError { print(categoryError); return }
do {
let drinks = try JSONDecoder().decode(Response.self, from: categoryData!).drinks
self.categories.append(Category(name: category, drinks: drinks))
} catch {
print(error)
}
}
categoryTask.resume()
}
group.notify(queue: .main) {
self.tableView.reloadData()
}
} catch {
print(error)
}
}.resume()
}
This is just a pseudocode, which will give you an idea how you can proceed further. The code has not been tested.
Create an array of sections to be loaded.
var sections: [Sections] = []
In you tableview delegates you can create a struct for the sections that you need to load, which will help you to identify the section in cell for row index path where you can call API based on categories.
extension ViewController: UITableViewDataSource, UITableViewDelegate {
struct Sections {
static var count = 0
// In stantiate table view headers index order
enum SectionType {
case SoftDrink
case OrdinaryDrink
case MilkShake
}
var type: SectionType?
var section: Int?
var rows: Int?
}
func setUpTableView() {
// Set Up Tableview Data
if check if Drink is type of SoftDrink /*If you sections are loaded dynamic u can add condition*/ {
sections.append(Sections(type: .SoftDrink, section: Sections.count, rows: 1))
Sections.count += 1
}
Sections.count = 0
}
func numberOfSections(in _: UITableView) -> Int {
sections.count
}
func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
sections[section].rows ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var tableCell: UITableViewCell = UITableViewCell()
guard let type = sections[indexPath.section].type else {
tableCell.selectionStyle = .none
return tableCell
}
switch type {
case .SoftDrink: break
// Instantiate cell and API calls.
case .OrdinaryDrink: break
// Instantiate cell and API calls.
case .MilkShake: break
// Instantiate cell and API calls.
}
tableCell.selectionStyle = .none
return tableCell
}
}
setUpTableView() can be called in viewDidLoad Method.
Basically I'm building an app that will consume data from a rest API.
In it's data also exists a couple images which came as URL's to be downloaded. So I though it would be better to download these images only once, then I cache them to reuse it on my UITableView Cells so on.
What should I do to use this feature correctly?
So let's get started. First I created the following class to handle download/cache images:
class ImageService {
static let cache = NSCache<NSString, UIImage>()
static func downloadImage(url:URL, completion: #escaping (_ image:UIImage?)->()) {
let dataTask = URLSession.shared.dataTask(with: url) { (data, response, error) in
var downloadedImage:UIImage?
if let data = data {
downloadedImage = UIImage(data: data)
}
if downloadedImage != nil {
self.cache.setObject(downloadedImage!, forKey: url.absoluteString as NSString)
}
DispatchQueue.main.async {
completion(downloadedImage)
}
}
dataTask.resume()
}
static func getImage(url:URL, completion:#escaping (_ image:UIImage?)->()) {
if let image = self.cache.object(forKey: url.absoluteString as NSString) {
completion(image)
} else {
downloadImage(url: url, completion: completion)
}
}
}
Facing the code above, in my UITableView class I just called:
class TableViewController: UITableViewController {
private var articlesViewModel:ArticleViewModel?
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.showsVerticalScrollIndicator = false
self.navigationController?.navigationBar.prefersLargeTitles = true
//Call Network
Networking.getApiData(url: Networking.urlRequest) { [weak self] (articles) in
//Data from API
self?.articlesViewModel = ArticleViewModel(modelRef: articles)
DispatchQueue.main.async {
self?.tableView.reloadData()
}
}
}
//MARK: TableView DataSource
override func numberOfSections(in tableView: UITableView) -> Int {
return 1 //Static
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.articlesViewModel?.updateTableCount() ?? 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.cellIdentifier, for: indexPath) as! TableViewCell
if let title = self.articlesViewModel?.updateTableTitleForIndex(indexpath: indexPath.row) {
cell.titleCell.text = title
}
if let description = self.articlesViewModel?.updateTableDescriptionForIndex(indexpath: indexPath.row) {
cell.descriptionCell.text = description
}
if let pictureURL = self.articlesViewModel?.updateTableImageForIndex(indexpath: indexPath.row) {
ImageService.getImage(url: URL(string: pictureURL)!) { (finalImage) in
cell.imageCell.image = finalImage
}
}
return cell
}
}
However, after I run the App I got the following result:
Seems that all cached images came before I get the result from API on ViewDidLoad
If I scroll down then up again, I got the following result:
Seems that everything got kinda "reordered", then everything looks ok.
Additional Classes:
ViewModel:
class ArticleViewModel {
static var articlesRef:[Article]!
init(modelRef:[Article]) {
ArticleViewModel.articlesRef = modelRef
}
//MARK: Functions
func updateTableCount() -> Int {
return ArticleViewModel.self.articlesRef.count
}
func updateTableTitleForIndex(indexpath:Int) -> String {
return ArticleViewModel.self.articlesRef[indexpath].title ?? ""
}
func updateTableDescriptionForIndex(indexpath:Int) -> String {
return ArticleViewModel.self.articlesRef[indexpath].description ?? ""
}
func updateTableImageForIndex(indexpath:Int) -> String {
return ArticleViewModel.self.articlesRef[indexpath].urlToImage ?? ""
}
}
Networking Layer:
class Networking {
static var urlRequest = "https://newsapi.org/v2/everything?q=apple&from=2019-06-05&to=2019-06-05&sortBy=popularity&apiKey=04d5f33acdde48f1a22a90f46fc483b5"
static func getApiData(url: String?, _ completion:#escaping([Article]) -> ()) {
guard let unrwpUrl = url else {return}
let request = URL(string: unrwpUrl)
URLSession.shared.dataTask(with: request!) { (data, request, error) in
if let data = data {
do {
let decodedData = try JSONDecoder().decode(Articles.self, from: data)
completion(decodedData.articles)
}catch{
print(error.localizedDescription)
}
}
}.resume()
}
}
Model:
struct Articles: Decodable {
let articles:[Article]
}
struct Article: Decodable {
let title:String?
let author:String?
let description:String?
let urlToImage:String?
}
I am getting
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
at DispatchQueue.main.async{ self.tableView.reloadData() }
My code
import UIKit
class BrowseRequestsViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
final let url = URL(string: "")
private var browseRequestDataModel = [Datum]()
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
downloadJson()
}
func downloadJson() {
guard let downloadURL = url else { return }
URLSession.shared.dataTask(with: downloadURL) { data, urlResponse, error in
guard let data = data, error == nil, urlResponse != nil else {
print("something is wrong")
return
}
print("downloaded")
do {
let decoder = JSONDecoder()
let downloadedBrowseRequestData = try decoder.decode(BrowseRequestsDataModel.self, from: data)
self.browseRequestDataModel = downloadedBrowseRequestData.data
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("something wrong after downloaded")
}
}.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return browseRequestDataModel.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "BrowseRequestsTableViewCell" ) as? BrowseRequestsTableViewCell else { return UITableViewCell() }
cell.shipperName.text = browseRequestDataModel[indexPath.row].user.name
cell.pickupLocation.text = browseRequestDataModel[indexPath.row].pickupLocation.pickupLocationCity + " , " + browseRequestDataModel[indexPath.row].pickupLocation.pickupLocationCountry
cell.dropoffLocation.text = browseRequestDataModel[indexPath.row].dropoffLocation.dropoffLocationCity + " , " + browseRequestDataModel[indexPath.row].dropoffLocation.dropoffLocationCountry
cell.item.text = browseRequestDataModel[indexPath.row].item.name
cell.pickupDate.text = browseRequestDataModel[indexPath.row].pickupDate
cell.dropoffDate.text = browseRequestDataModel[indexPath.row].dropoffDate
return cell
}
}
I have been working on a launch database for SpaceX and I have successfully parsed my data but the function to create and add the data to the cell is not working. I have added the delegates and data sources but I still cannot find out why it won't run.
import UIKit
struct launchData : Decodable
{
let flight_number : Int
let launch_date_utc : String
struct rocketInfo : Decodable
{
let rocket_name : String
}
let rocket : rocketInfo
}
class LaunchViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var launchTableView: UITableView!
var arrayOfLaunchData:[launchData] = []
override func viewDidLoad()
{
super.viewDidLoad()
self.launchTableView.delegate = self
self.launchTableView.dataSource = self
getJsonData()
self.launchTableView.reloadData()
}
func getJsonData()
{
let jsonUrlString = "https://api.spacexdata.com/v2/launches"
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let launchDataDecoded = try JSONDecoder().decode([launchData].self, from: data)
print(launchDataDecoded)
} catch let jsonErr {
print("Error Serialization json:", jsonErr )
}
}.resume()
print("getJsonData ran")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("")
print(arrayOfLaunchData.count)
print("")
print("TableView number of rows ran")
return arrayOfLaunchData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellID")
let launch = self.arrayOfLaunchData[indexPath.row]
let flightNumber = launch.flight_number
let rocketName = launch.rocket.rocket_name
cell?.textLabel?.text = "Mission " + String(flightNumber)
let launchDate = launch.launch_date_utc
cell!.detailTextLabel!.text = "Launch Date: " + launchDate + "Rocket Used: " + rocketName
self.launchTableView.reloadData()
print("TableView cellForRowAt ran")
return cell!
}
}
First of all never call reloadData() in cellForRowAt! Delete the line
Two major issues:
reloadData() is called too soon.
The data source array is not populated after receiving the data.
The solution is to delete the line
self.launchTableView.reloadData()
(also) in viewDidLoad() and change getJsonData() to
func getJsonData()
{
let jsonUrlString = "https://api.spacexdata.com/v2/launches"
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
self.arrayOfLaunchData = try JSONDecoder().decode([launchData].self, from: data)
print(launchDataDecoded)
DispatchQueue.main.async {
self.launchTableView.reloadData()
}
} catch {
print("Error Serialization json:", error )
}
}.resume()
print("getJsonData ran")
}
because dataTask works asynchronously.
Note:
Please conform to the naming convention that struct and class names start with a capital letter (LaunchData, RocketInfo) and all names are supposed to be camelCased rather than snake_cased.
Remove self.launchTableView.reloadData() from viewDidLoad()
and put on getting successfully data
do {
let launchDataDecoded = try JSONDecoder().decode([launchData].self, from: data)
print(launchDataDecoded)
self.launchTableView.reloadData()
} catch let jsonErr {
print("Error Serialization json:", jsonErr )
}
}.resume()
getJsonData() is follow asynchronous. hope this help!
I'm trying to load the image async in my tableview, to be able to read them 'offline'.
The images display correctly when i'm having a connexion. otherwise, it doesn't when I don't have any connexion so its not working. . .
here is the code i am using:
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import SwiftKeychainWrapper
import SwiftyJSON
var posts = [Post]()
var selectedIndexPath: Int = 10
class FeedVC: UIViewController, UITableViewDataSource,
UIImagePickerControllerDelegate, UINavigationControllerDelegate,
UITableViewDelegate {
#IBOutlet weak var feedTableView: UITableView!
private var readPosts: ObserveTask?
override func viewDidLoad() {
super.viewDidLoad()
feedTableView.dataSource = self
feedTableView.delegate = self
readPosts = Post.observeList(from: Post.parentReference.queryOrdered(byChild: "privacy").queryEqual(toValue: false))
{
observedPosts in
posts = observedPosts.reversed()
self.feedTableView.reloadData()
}
}
override var prefersStatusBarHidden: Bool {
return true
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.feedTableView.dequeueReusableCell(withIdentifier: "MessageCell")! as UITableViewCell
let imageView = cell.viewWithTag(1) as! CustomImageView
let titleLabel = cell.viewWithTag(2) as! UILabel
let linkLabel = cell.viewWithTag(3) as! UILabel
titleLabel.text = posts[indexPath.row].title
titleLabel.numberOfLines = 0
linkLabel.text = posts[indexPath.row].link
linkLabel.numberOfLines = 0
Storage.getImage(with: posts[indexPath.row].imageUrl){
postPic in
imageView.image = postPic
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard posts[indexPath.row].title != "COMING SOON" else {
return
}
selectedIndexPath = indexPath.row
self.performSegue(withIdentifier: "push", sender: self)
self.feedTableView.reloadData() }
}
let imageCache = NSCache<AnyObject, AnyObject>()
class CustomImageView: UIImageView{
var imageUrlString: String?
func loadImageUsingUrlString(urlString: String){
imageUrlString = urlString
let url = NSURL(string: urlString)
image = nil
if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage{
self.image = imageFromCache
return
}
URLSession.shared
.dataTask(with: url! as URL, completionHandler:
{(data, respones, error) in
if error != nil{
print(error)
return
}
DispatchQueue.main.sync(execute: {
let imageToCache = UIImage(data: data!)
if self.imageUrlString == urlString{
self.image = imageToCache
}
imageCache.setObject(imageToCache!, forKey: urlString as AnyObject)
self.image = imageToCache
}
)
}).resume()
}
}
I'm having my async function from the class class CustomImageView.
I'm usually calling the image via:
Storage.getImage(with: posts[indexPath.row].imageUrl){
postPic in
imageView.image = postPic
}
I suppose the problem is coming from that part ? ( i am not sur)
Does anybody have any highlight of what is wrong within my process ?
Thanks a lot for your help !!
-- EDIT --
the image storage is being call via:
import Foundation
import FirebaseStorage
import UIKit
let STORAGE_BASE = FIRStorage.storage().reference()
class Storage
{
static let REF_POST_IMAGES = STORAGE_BASE.child("post-pics")
static let REF_USER_IMAGES = STORAGE_BASE.child("user-pics")
static let imageCache: NSCache<NSString, UIImage> = NSCache()
static func upload(image: UIImage, to reference: FIRStorageReference, completionHandler: #escaping (String?, Error?) -> ())
{
// Uploads the image
if let imageData = UIImageJPEGRepresentation(image, 0.2)
{
let imageUid = NSUUID().uuidString
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpeg"
reference.child(imageUid).put(imageData, metadata: metadata)
{
(metadata, error) in
if let error = error
{
completionHandler(nil, error)
}
if let downloadURL = metadata?.downloadURL()?.absoluteString
{
// Caches the image for faster display
imageCache.setObject(image, forKey: downloadURL as NSString)
completionHandler(downloadURL, nil)
}
}
}
else
{
completionHandler(nil, StorageError.compressionFailed)
}
}
// Storage.imageCache.object(forKey: post.imageUrl as NSString)
static func getImage(with url: String, completionHandler: #escaping (UIImage) -> ())
{
if let image = imageCache.object(forKey: url as NSString)
{
completionHandler(image)
}
else
{
let ref = FIRStorage.storage().reference(forURL: url)
ref.data(withMaxSize: 2 * 1024 * 1024)
{
(data, error) in
if let error = error
{
print("STORAGE: Unable to read image from storage \(error)")
}
else if let data = data
{
print("STORAGE: Image read from storage")
if let image = UIImage(data: data)
{
// Caches the image
Storage.imageCache.setObject(image, forKey: url as NSString)
completionHandler(image)
}
}
}
}
}
}
enum StorageError: Error
{
case compressionFailed
}