So I am using kingfisher as people have suggested its the way to go with remote images.
However I have a func that is meant to load now playing info
func nowplaying(){
let jsonURLString = "https://api.drn1.com.au/station/playing"
guard let feedurl = URL(string: jsonURLString) else { return }
URLSession.shared.dataTask(with: feedurl) { (data,response,err)
in
guard let data = data else { return }
do{
let nowplaying = try JSONDecoder().decode(Nowplayng.self, from: data)
nowplaying.data.forEach {
self.artist.text = $0.track.artist
self.song.text = $0.track.title
//self.artist.textAlignment = .center
//self.song.textAlignment = .center
print($0.track.title)
if let strUrl = nowplaying.data.first?.track.imageurl {
self.imageurl.kf.setImage(with: URL(string: strUrl), placeholder: nil)
}
//self.imageurl.setImage(with: $0.track.imageurl)
}
}catch let jsonErr{
print("error json ", jsonErr)
}
}.resume()
}
but while the song title and artist shows the image does not.
Actually url contains spaces that need to be percentage encoding.
if var strUrl = nowplaying.data.first?.track.imageurl {
strUrl = strUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
self.imageurl.kf.setImage(with: URL(string: strUrl), placeholder: nil)
}
Related
So I have the following code printing out the following
[DRN1.Data(track: DRN1.Trackinfo(title: "Charly\'s Ballad (Original Mix)", artist: "Castle Queenside", imageurl: "covers.drn1.com.au/az_B1017197_Disc 1 Traxsource Nu Disco & Indie Dance_Castle Queenside.jpg"))]
However when I go to write
print(nowplaying.data.track.title)
I get errors and it won't even attempt to load the swift app
struct Nowplayng: Decodable{
let data: [Data]
}
struct Data: Decodable{
let track: Trackinfo
}
struct Trackinfo: Decodable {
let title: String
let artist: String
let imageurl: String
}
works
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let jsonURLString = "https://api.drn1.com.au/station/playing"
guard let feedurl = URL(string: jsonURLString) else { return }
URLSession.shared.dataTask(with: feedurl) { (data,response,err)
in
guard let data = data else { return }
do{
let nowplaying = try JSONDecoder().decode(Nowplayng.self, from: data)
print(nowplaying.data)
}catch let jsonErr{
print("error json ", jsonErr)
}
// let dataAsString = String(data:data, encoding: .utf8)
// print(dataAsString)
}.resume()
}
does not work
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let jsonURLString = "https://api.drn1.com.au/station/playing"
guard let feedurl = URL(string: jsonURLString) else { return }
URLSession.shared.dataTask(with: feedurl) { (data,response,err)
in
guard let data = data else { return }
do{
let nowplaying = try JSONDecoder().decode(Nowplayng.self, from: data)
print(nowplaying.data.track.title)
}catch let jsonErr{
print("error json ", jsonErr)
}
// let dataAsString = String(data:data, encoding: .utf8)
// print(dataAsString)
}.resume()
}
data is an array you need to loop over it
nowplaying.data.forEach {
print($0.track.title)
}
If you care about the first item do
if let item = nowplaying.data.first {
print(item.track.title)
}
I want to load image in View did load method without use of Tableview or collectionView , I have URL of Wordpress Rest Api , I tried to load Image using SwiftyJson , I got Row Values of All Images But Image is not Able to Load I Found fatal error: Index out of range, please help me
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: " ")
let session = URLSession(configuration: URLSessionConfiguration.default)
let task = session.dataTask(with: URLRequest(url: url! as URL)) {(data,response , error) -> Void in
if (error == nil)
{
let swiftyJSON = JSON(data!)
let entrylevel = swiftyJSON["guid"].arrayValue
print("\(String(describing: entrylevel))")
let imagearray = entrylevel[0]["rendered"].string!
let imageurl = NSURL(string:imagearray)
if let imagedata = NSData(contentsOf: imageurl! as URL)
{
self.img.image = UIImage(data: imagedata as Data)
print("image load")
}
}
}
task.resume()
}
and here is my rest api
[{"id":1509 "date":"2018-02-15T13:33:22",
"date_gmt":"2018-02-15T13:33:22" "guid": {
"rendered":"http://thenewschef.com/wp-content/uploads/2018/02/startup.jpeg"
}
guid is a dictionary not array try
let entrylevel = swiftyJSON[0]["guid"]["rendered"].string
also wrap this in
DispatchQueue.main.async {
self.img.image = UIImage(data: imagedata as Data)
}
and it's better using Data instead of NSData
//
OR
struct Root:Decodable {
let guid:InnerItem
}
struct InnerItem:Decodable {
let rendered:String
}
try {
let items = try JSONDecoder().decode([Root].self, from: data!)
}
catch {
print(error)
}
How To Download PDF file in IOS using Swift 3.0 and Alamofire . i can able to fetch url with nsurlsession. But i am looking for alamofire code.
please see my code.
func downloadPdffile(_ sender : UIButton) {
print(sender.tag)
print("ARRAY VALUES FROM CELL",totalSyllabusArray.object(at: sender.tag))
var localDic :NSDictionary!
localDic = totalSyllabusArray.object(at: sender.tag) as! NSDictionary
let filepath = localDic["filepath"] as! String
print("pressed ")
let strURL1:String = FETCH_InMegh_Image_BaseURL + filepath
print("strURL1 is ",strURL1)
let pathURL = URL(string: strURL1)!
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = try! URLRequest(url: pathURL, method: .get)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Success: \(statusCode)")
print("tempLocalUrl: \(tempLocalUrl)")
} else {
print("Failure: %#", error?.localizedDescription);
}
}
}
}
}
Define your destination something like that:
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent("your.pdf")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
And call Alamofire.download with your url and destination:
Alamofire.download(yourUrl), to: destination).response { response in
let parentView = (self.superview?.superview as! UITableView).dataSource as! ProcedureViewController
parentView.hideActivityIndicator()
if response.error == nil, let _ = response.destinationURL?.path {
//open pdf in UIDocumentInteractionController
self.docController = UIDocumentInteractionController.init(url: response.destinationURL!)
self.docController?.delegate = self.delegate!
self.docController?.name = ""
self.docController?.presentOptionsMenu(from: self.parentView!.bounds, in: self.parentView!, animated: true)
}
}
I'm woking on a project in swift 3.0 where I cache the response from the server by using NSCache as to populate them in a UITableView. However for some reason I'm only seeing few images loading when the app loads for the first time, but if If i scroll and come back I see everything (end of retrieving the response from the server I reload my tableview too, but seems that not the case). I'm not sure what I''m exactly missing here, the code as bellow as to show how I cache the images.
let imageCache = NSCache<AnyObject, AnyObject>()
var imageURLString : String?
extension UIImageView {
public func imageFromServerURL(urlString: String) {
imageURLString = urlString
if let url = URL(string: urlString) {
image = nil
if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = imageFromCache
return
}
URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
if error != nil{
print(error as Any)
return
}
DispatchQueue.main.async(execute: {
if let imgaeToCache = UIImage(data: data!){
if imageURLString == urlString {
self.image = imgaeToCache
}
imageCache.setObject(imgaeToCache, forKey: urlString as AnyObject)// calls when scrolling
}
})
}) .resume()
}
}
}
I think this would be a better approach using subclassing rather than extension, (taking help from Jageen's comment, as we cannot contain stored properties inside extension so we use the idea of encapsulation)
let imageCache = NSCache<AnyObject, AnyObject>()
class CustomImageView: UIImageView {
var imageUrlString: String?
func loadImageUsingUrlString(_ urlString: String) {
let url = URL(string: urlString)
imageUrlString = urlString
image = nil
if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = imageFromCache
return
}
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
let imageToCache = UIImage(data: data!)
if self.imageUrlString == urlString {
self.image = imageToCache
}
imageCache.setObject(imageToCache!, forKey: urlString as AnyObject)
}
}.resume()
}
}
-Now use this subclass as the type of imageViews that you are showing on the screen
Here the images are downloading and stored in cache just fine. The problem lies in the updation of tableview cells.
When the table view is loading the cells on to the table the images are not downloaded yet. But once the image is downloaded we have to selectively update the cell so that the image is displayed instantly.
Since you are scrolling , the tableview calls 'cellForRowatIndexpath' again which updates the cell showing the downloaded images while scrolling.
If you still wish to use the extension , I suggest you add the tableView and indexpath as the parameters so that we can call reload specific row and have the view updated instantly.
I have updated the table reload code and structure of the function defined in extension. Let me know how it goes.
let imageCache = NSCache<AnyObject, AnyObject>()
var imageURLString : String?
extension UIImageView {
public func imageFromServerURL(urlString: String, tableView : UITableView, indexpath : IndexPath)) {
imageURLString = urlString
if let url = URL(string: urlString) {
image = nil
if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = imageFromCache
return
}
URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
if error != nil{
print(error as Any)
return
}
DispatchQueue.main.async(execute: {
if let imgaeToCache = UIImage(data: data!){
if imageURLString == urlString {
self.image = imgaeToCache
}
imageCache.setObject(imgaeToCache, forKey: urlString as AnyObject)// calls when scrolling
tableView.reloadRows(at: [indexpath], with: .automatic)
}
})
}) .resume()
}
}
Saving Images in UIImageView Swift 5 with Xcode 14.1 and above through URLCache :-
class CacheImageView: UIImageView {
let cachesURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
var diskCacheURL:URL {
self.cachesURL.appendingPathComponent("DownloadCache")
}
var cache:URLCache {
URLCache(memoryCapacity: 10_000_000, diskCapacity: 1_000_000_000, directory: diskCacheURL)
}
var session:URLSession {
let config = URLSessionConfiguration.default
config.urlCache = cache
return URLSession(configuration: config)
}
func downloadImageFrom(urlString: String, imageMode: UIView.ContentMode) {
guard let url = URL(string: urlString) else { return }
downloadImageFrom(url: url, imageMode: imageMode)
}
func downloadImageFrom(url: URL, imageMode: UIView.ContentMode) {
contentMode = imageMode
let req = URLRequest(url: url, cachePolicy: .returnCacheDataElseLoad)
self.session.dataTask(with: req) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async {
let imageToCache = UIImage(data: data)
self.image = imageToCache
}
}.resume()
}
}
Uses:
var imageViewAstronomy: CacheImageView = CacheImageView()
imageViewAstronomy.downloadImageFrom(urlString: yourStringUrlOfImage, imageMode: .scaleAspectFit)
I am trying to complet an an action after the URLSession resumes.
So I am downloading several images from my server with the url, which all works good. But now I am trying to save those images to the disk after I have finished downloading them.
Problem
Now I can save them inside the same query while downloading them but I would prefer not too as it makes my query slower.
So I have added a completion handler to my func with the query, but when I save the images to the disk in that block it works but I cannot do anything with my screen as the query has not resumed yet it is blocked from touches I guess...
Now I would like to be able to call my func to save the images to the disk straight after the query has been resumed.... Anyone have any idea?
If someone needs more explanation or to see code just drop a comment below
Many thanks in advance to anyone that can help!
Code for downloading
func loadPosts(completionHandler: #escaping (Bool) -> ()) {
pageNumber = 1
appDelegate.setNetworkActivityIndicatorVisible(true)
let id = user!["id"] as! String
let url = URL(string: "http://************/Files/Posts.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let body = "id=\(id)&caption=&uuid=&page="
request.httpBody = body.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data:Data?, response:URLResponse?, error:Error?) in
DispatchQueue.global(qos: .background).async {
if error == nil {
let oldImageArray = self.cellContentArray
self.cellContentArray.removeAll(keepingCapacity: false)
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
guard let parseJSON = json else {
print("Error while parsing")
return
}
guard let posts = parseJSON["Posts"] as? [AnyObject] else {
print("Error while parseJSONing")
return
}
for element in posts {
// here I download the stuff and it to my Array, too long and no point to show here
}
let oldImageSet = Set(oldImageArray.map({return $0.uuid}))
let newImageSet = Set(self.cellContentArray.map({return $0.uuid}))
let addedImages = newImageSet.subtracting(oldImageSet)
let addedImageSections = Array(addedImages).map{ self.cellContentArray.map({return $0.uuid}).index(of: $0)! }
let addedImageIndexSet = IndexSet(addedImageSections)
let removedImages = oldImageSet.subtracting(newImageSet)
let removedImageSections = Array(removedImages).map{ oldImageArray.map({return $0.uuid}).index(of: $0)! }
let removedImageIndexSet = IndexSet(removedImageSections)
if !addedImageIndexSet.isEmpty {
if oldImageArray.count >= 5 {
self.lastUUIDImage = oldImageArray[4].uuid
} else {
}
self.coreDataShit()
}
DispatchQueue.main.async{
print(placeholderImage.count)
if placeholderImage.count > 5 {
placeholderImage.removeFirst(placeholderImage.count - 5)
}
print("finished")
self.customView.isHidden = true
if posts.count >= 5 {
self.tableView.addInfiniteScroll { [weak self] (scrollView) -> Void in
self?.loadMore()
}}
self.activityView.stopAnimating()
self.internetView.removeFromSuperview()
self.tableView.beginUpdates()
if !addedImageIndexSet.isEmpty {
self.tableView.insertSections(addedImageIndexSet, with: .top)
}
if !removedImageIndexSet.isEmpty {
self.tableView.deleteSections(removedImageIndexSet, with: .bottom)
}
self.tableView.endUpdates()
self.tableView.finishInfiniteScroll()
self.refresher.endRefreshing()
appDelegate.setNetworkActivityIndicatorVisible(false)
completionHandler(true)
}
} catch {
DispatchQueue.main.async {
self.tableView.removeInfiniteScroll()
self.customView.isHidden = false
self.refresher.endRefreshing()
self.tableView.reloadData()
}
}
} else {
DispatchQueue.main.async(execute: {
let message = error!.localizedDescription
appDelegate.infoView(message: message, color: smoothRedColor)
})
}
}
})
task.resume()
}
Saving Image
self.loadPosts(completionHandler: { (true) in
print("completion")
let sections = self.tableView.numberOfSections
for i in 0..<sections {
self.rows += self.tableView.numberOfRows(inSection: i)
}
print(self.rows)
if self.rows <= 5 {
print("less than 5")
print(self.rows)
var i = 0
for element in self.cellContentArray {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let dirPath = "\(path)/images"
let url = NSURL(fileURLWithPath: dirPath)
let filePath = url.appendingPathComponent("\(element.uuid).jpg")?.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath!) {
print("File exsists")
} else {
print("File doesn't exsist")
DispatchQueue.main.async {
let url = NSURL(string: element.fullImage!)! // convert path str to url
let imageData = NSData(contentsOf: url as URL) // get data via url and assigned imageData
let imageName = element.uuid
let saveImages = FileSaveHelper(fileName: imageName, fileExtension: .JPG, subDirectory: "images", directory: .documentDirectory)
do {
guard let image = UIImage.sd_image(with: imageData as Data!) else {
print("Error getting image")
return
}
try saveImages.saveFile(image: image)
self.saveNewImagePath(imageLink: imageName, uuid: imageName)
self.removeImage(itemName: "file\(i)", fileExtension: "jpg")
self.removeImage(itemName: self.lastUUIDImage, fileExtension: "jpg")
i += 1
} catch {
print(error)
}
}
}
}
}
})
Image in tableView Cell
self.postImage.sd_setImage(with: URL(string: content.fullImage!), placeholderImage: placeHolder, options: .retryFailed) { (image:UIImage?, error:Error?, cached:SDImageCacheType, url:URL?) in
}
In your code of saving image this line of code is blocking you UI
let imageData = NSData(contentsOf: url as URL) // get data via url and assigned imageData
This is not proper way to download image from server, you should download image asynchronously using URLSession