Swift 5 remote image loads in UITableViewCell on click not on load - ios

I have a tableview cell that does not load properly. when it loads it is supposed to load the title and the image at the same time, like any normal page. as of now it only loads the images when you click on the cell. and when you click the image it is larger than it's supposed to be. not sure why. it's similar code used in another part of the project and I don't have that issue there. so there it is. not sure if what's going on is related to all those constraints I haven't set yet.
the project is an Amazon clone. the data is called from an api and the images are given as url strings. so I'm loading images from urls and placing them into my image views.
here's a video of what is going on. in the video I wait a few seconds before clicking on the cell to give it a chance to load on its own. in the code where the image is processed from the url there is a print statement that fires around the same time the title is generated so I know the image is created. it's just waiting until it is clicked to appear. not sure why. https://gfycat.com/talkativebackhornet
the page where the code is loaded. this code performs a search and uses the results to form the ProductsDetails struct where the data is displayed. this is basically just the Amazon page you get when you select a product. the image view will later be converted into a horizontal scrolling view once the code is working.
class ProductViewController: UITableViewController {
var asinForSearch: String = ""
var resultsManager = ResultsManager()
var productsDetails = Array<Products>()
{
didSet {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
resultsManager.detailsDelegate = self
tableView.dataSource = self
tableView.register(UINib(nibName: "ImagesCell", bundle: nil), forCellReuseIdentifier: "imagesCell")
tableView.rowHeight = 750
populateDetailsPage()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productsDetails.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let productResults = productsDetails[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "imagesCell", for: indexPath) as! ImagesCell
let url = URL(string: productResults.mainImage)
cell.imagioView.kf.setImage(with: url)
cell.title.text = productResults.title
return cell
}
func populateDetailsPage(){
resultsManager.getDetails(asinForSearch)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
extension ProductViewController: ResultsDetailDelegate {
func updateDetails(_ resultsManager: ResultsManager, products: Products){
self.productsDetails.append(products)
}
}
here's the code that creates the cell. not sure if this is necessary.
class ImagesCell: UITableViewCell, UIScrollViewDelegate {
#IBOutlet weak var imageScrollView: UIScrollView!
#IBOutlet weak var title: UILabel!
#IBOutlet weak var imagioView: UIImageView!
#IBOutlet weak var view: UIView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
imageScrollView.delegate = self
imageScrollView.contentSize = CGSize(width: imagioView.frame.width, height: imagioView.frame.height)
self.imageScrollView.addSubview(imagioView)
view.addSubview(imageScrollView)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
func getDetails(_ asin: String){
let search = "\(detailRequest)\(asin)&country=US"
var request = URLRequest(url: URL(string: search)!)
request.httpMethod = "GET"
request.allHTTPHeaderFields = params
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error -> Void in
if let jsonData = data {
do {
let root = try JSONDecoder().decode(ProductDetail.self, from: jsonData)
if let productDetails = self.parseJSONDetails(root.product) {
self.detailsDelegate?.updateDetails(self, products: productDetails)
}
} catch {
print(error)
}
}
}
task.resume()
}
func parseJSONDetails(_ safeData: Products) -> Products? {
do {
let products = Products(asin: safeData.asin, deliveryMessage: safeData.deliveryMessage, productDescription: safeData.productDescription, featureBullets: safeData.featureBullets, images: safeData.images, mainImage: safeData.mainImage, price: safeData.price, productInformation: safeData.productInformation, title: safeData.title, totalImages: safeData.totalImages, totalVideos: safeData.totalVideos, url: safeData.url)
return products
} catch {
print()
}
}
let me know if there's any other code you need to see

You need to let your tableView know that there is new data and it needs to update its cells.
Call self.tableView.reloadData() when the delegate announces that it finished getting products.
extension ProductViewController: ResultsDetailDelegate {
func updateDetails(_ resultsManager: ResultsManager, products: Products){
self.productsDetails.append(products)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
Remove the reloading from here:
func populateDetailsPage(){
resultsManager.getDetails(asinForSearch)
}
This whole function is useless. It doesn't do anything:
func parseJSONDetails(_ safeData: Products) -> Products? {
do {
let products = Products(asin: safeData.asin, deliveryMessage: safeData.deliveryMessage, productDescription: safeData.productDescription, featureBullets: safeData.featureBullets, images: safeData.images, mainImage: safeData.mainImage, price: safeData.price, productInformation: safeData.productInformation, title: safeData.title, totalImages: safeData.totalImages, totalVideos: safeData.totalVideos, url: safeData.url)
return products
} catch {
print()
}
}
There is no reason why you would instantiate a Products object from another Products. At least, I can't understand why it is needed.
You have this ProductDetail object after you make the network request, then just pass its parameter .products to the delegate:
func getDetails(_ asin: String){
let search = "\(detailRequest)\(asin)&country=US"
var request = URLRequest(url: URL(string: search)!)
request.httpMethod = "GET"
request.allHTTPHeaderFields = params
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
let task = session.dataTask(with: request) { [weak self] data, response, error -> Void in
guard let self = self else { return }
if let jsonData = data {
do {
let productDetail = try JSONDecoder().decode(ProductDetail.self, from: jsonData)
let products = productDetail.products
self.detailsDelegate?.updateDetails(self, products: products)
} catch {
print(error)
}
}
}
task.resume()
}
Also, make sure your detailsDelegate is declared as weak var, otherwise you risk creating a retain cycle.

Related

Swift. I pull out pictures in cells through api. Everything is being built, but instead of pictures it's empty. Returns nil. Explain please

I'm a beginner. I pull out pictures in cells through api. Everything is built, but instead of pictures - it's empty. Returns nil. I've been sitting here all day and can't figure it out!
API link - https://swiftbook.ru//wp-content/uploads/api/api_courses
If this answer is somewhere, I apologize, and if it's not difficult to give a link, send it please, thank you.
Thank you very much in advance for your help and clarification!!!
enter image description here
import UIKit
class CourseCell: UITableViewCell {
#IBOutlet var courseImage: UIImageView!
#IBOutlet var courseNameLabel: UILabel!
#IBOutlet var numberOfLessons: UILabel!
#IBOutlet var numberOfTests: UILabel!
func configure(with course: Course) {
courseNameLabel.text = course.name
numberOfLessons.text = "Number of lessons \(course.number_of_lessons ?? 0)"
numberOfTests.text = "Number of tests \(course.number_of_tests ?? 0)"
DispatchQueue.global().async {
guard let stringUrl = course.imageUrl,
let imageURL = URL(string: stringUrl),
let imageData = try? Data(contentsOf: imageURL)
else {
return
}
DispatchQueue.main.async {
self.courseImage.image = UIImage(data: imageData)
}
}
}
}
Model for decode by JSON
Course.swift
struct Course: Decodable {
let name: String?
let imageUrl: String?
let number_of_lessons: Int?
let number_of_tests: Int?
}
struct WebsiteDescription: Decodable {
let courses: [Course]?
let websiteDescription: String?
let websiteName: String?
}
And piece of code with JSON from CoursesViewController.swift
extension CoursesViewController {
func fetchCourses() {
guard let url = URL(string: URLExamples.exampleTwo.rawValue) else { return }
URLSession.shared.dataTask(with: url) { (data, _, _) in
guard let data = data else {
return
}
do {
// получаем курсы в переменную
self.courses = try JSONDecoder().decode([Course].self, from: data)
// и мы должны перезагрузить таблицу
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch let error {
print(error)
}
}.resume()
}
}
And here is i get nil probably (please see screenshot below)
enter image description here
I tried to make another version of your code and it's able to run. You can check my code and compare with your own.
CoursesViewController
class CoursesViewController: UIViewController {
private lazy var tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .plain)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.register(CourseCell.self, forCellReuseIdentifier: "CourseCell")
tableView.delegate = self
tableView.dataSource = self
return tableView
}()
private var courses: [Course] = []
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupLayout()
fetchCourses()
}
private func setupViews() {
view.addSubview(tableView)
}
private func setupLayout() {
tableView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
private func fetchCourses() {
guard let url = URL(string: "https://swiftbook.ru//wp-content/uploads/api/api_courses") else {
return
}
URLSession.shared.dataTask(with: url) { (data, _, _) in
guard let data = data else {
return
}
do {
// получаем курсы в переменную
self.courses = try JSONDecoder().decode([Course].self, from: data)
// и мы должны перезагрузить таблицу
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch let error {
print(error)
}
}.resume()
}
}
extension CoursesViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
courses.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "CourseCell", for: indexPath) as? CourseCell else {
return UITableViewCell()
}
cell.configure(with: courses[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
120
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
120
}
}
Cell
class CourseCell: UITableViewCell {
private lazy var nameLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textColor = .black
label.font = .systemFont(ofSize: 14, weight: .bold)
return label
}()
private lazy var courseImage = UIImageView(frame: .zero)
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(with course: Course) {
nameLabel.text = course.name
DispatchQueue.global().async {
guard let stringUrl = course.imageUrl,
let imageURL = URL(string: stringUrl),
let imageData = try? Data(contentsOf: imageURL)
else {
return
}
DispatchQueue.main.async {
// Make sure it's the same course
self.courseImage.image = UIImage(data: imageData)
}
}
}
private func setupViews() {
courseImage.contentMode = .scaleAspectFill
contentView.addSubview(nameLabel)
contentView.addSubview(courseImage)
}
private func setupLayout() {
nameLabel.snp.makeConstraints { make in
make.top.leading.trailing.equalToSuperview().inset(8)
}
courseImage.snp.makeConstraints { make in
make.centerX.equalToSuperview().inset(8)
make.top.equalTo(nameLabel.snp.bottom).offset(12)
make.height.width.equalTo(80)
}
}
}
In my opinion, you should check your UI layout to make sure that the image view can be loaded and displayed properly.
Some Improvement suggestion
Course.swift: Please use lower camel case convention for variables name because it's the common Swift convention
CourseCell.swift: Since the course don't have ID so after a while you load image from background, this cell might be used by another because of the reuse cell mechanism.
DispatchQueue.main.async {
// Make sure it's the same course
if course.id == self.course.id {
self.courseImage.image = UIImage(data: imageData)
}
}
Use caching mechanism every time you load image from the server so that next time you don't need to fetch from the server again (you can set timeout for cache)
Instead of handing loading image by yourself, you can use well-known 3rd party libs like SDWebImage or KingFisher.
The answer above is Excellent !!! It's an additional valuable experience for me!
But main reason was in blocked .ru domains in my country. WHEN I ACCIDENTALLY TURNED ON THE VPN ON THE MAC AND BUILD APP, THEN EVERYTHING LOADED!!! Because I am in Ukraine now, and we have all .ru domains blocked, and the API URL is just on .ru

SwiftyJSON and Alamofire data parsing in TableView (iOS, Swift 4)

When I run my project I am just able to get only tableView but datas are not fetched its blank. The struct codable and view controller codes were as follows. Please help me in viewing the datas in my tableView cells using alamofire and SwiftyJSON,
class Loads: Codable {
let loads: [Load]
init(loads: [Load]) {
self.loads = loads
}
}
class Load: Codable {
let id: String
let ad_title: String
let ad_description:String
let ad_price: String
let ad_post_date: String
let image1: String
init(ad_title: String, ad_description: String, ad_price: String, ad_post_date: String, image1: String) {
self.ad_title = ad_title
self.ad_description = ad_description
self.ad_price = ad_price
self.ad_post_date = ad_post_date
self.image1 = image1
}
}
View Controller codes:
import UIKit
import SwiftyJSON
import Alamofire
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var loads = [Load]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
downloadJson()
self.tableView.reloadData()
tableView.delegate = self
tableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return loads.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "LoadCell") as? LoadCell else { return UITableViewCell() }
cell.labelA.text = loads[indexPath.row].ad_title
cell.labelB.text = loads[indexPath.row].ad_price
cell.labelC.text = loads[indexPath.row].ad_description
cell.labelD.text = loads[indexPath.row].ad_post_date
if let imageURL = URL(string: loads[indexPath.row].image1) {
DispatchQueue.global().async {
let data = try? Data(contentsOf: imageURL)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
cell.loadImage.image = image
}
}
}
}
return cell
}
func downloadJson(){
Alamofire.request("https://alot.ae/api/classifiedcomputerlaptop.php").responseJSON { response in
if let value = response.result.value {
let json = JSON(value)
//Printing strings from a JSON Dictionary
print(json[0]["ad_title"].stringValue)
print(json[0]["ad_price"].stringValue)
print(json[0]["ad_description"].stringValue)
print(json[0]["ad_post_date"].stringValue)
print(json[0]["image1"].stringValue)
}
self.tableView.reloadData()
}
self.tableView.reloadData()
}
}
I am using xcode9, swift 4.
datas are not fetched its blank
Your code does not seem to update var loads by downloaded data, that is why you just get blank table view. So, you need to assign fetched data to var loads.
Here is sample:
Alamofire.request("https://alot.ae/api/classifiedcomputerlaptop.php").responseJSON { response in
// you should assign response data into var loads here
if let data = response.data {
do {
self.loads = try JSONDecoder().decode([Load].self, from: data)
} catch {
// exception
}
}
}
And after that, self.tableView.reloadData().
PS: Of course I don't know your response JSON format and your source code overall, so it might not be a directory answer to your question, but I hope it will help!
At first glance, the url looks faulty to me. https is typed twice, please remove once in below line.
Change this :
Alamofire.request("https:https://alot.ae/api/classifiedcomputerlaptop.php").responseJSON
to :
Alamofire.request("https://alot.ae/api/classifiedcomputerlaptop.php").responseJSON
First off, your...
struct codable
... is not a struct at all, it is a class. change that to an actual struct.
Second, you start downloading before your delegate and datasource are set to the tableview.
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
downloadJson()
}
Thirdly, you are using Alamofire for a simple GET request, but you ignore the fact of asyncronous loading of images for a cell, which you do with Data. I'd suggest to use AlamoFireImageDownloader or remote AlamoFire all together. Using URLSession is just as easy:
private func request(url: URL, completionHandler: #escaping ((Data?, URLResponse?, Error?) -> Void)) {
var request = URLRequest(url: url)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request, completionHandler: completionHandler)
task.resume()
}
Fourth, I don't believe you need the loads codable.
Just an extra reminder, depending on the amount of Loads you'll have in your table, you will get problems with the loading of your images. The closures are not guaranteed to finish in order, but when they do they will update the cell regardless to whether the cell is already reused for another Load or not. Just FYI.

Swift 3: Preload data before putting it into UITableView

I have an app that will fetch exactly 100 strings from an API and place them into a UITableView. I wish to first preload the data into an array and then, once the array is fully populated with the 100 entries, load the data into the table.
Due to the asynchronous API call, it seems like I am unable to load data into the array before the table view starts populating its cells. Mainly, I am having difficulty getting the data out of the closure in the first place.
This is the API call defined in an APIAgent class:
func getAPIData(_ requestType: String, completionHandler: #escaping (Data) -> ()) {
let requestURL: URL = URL(string : baseURL + requestType)!
let currentSession = URLSession.shared
let task = currentSession.dataTask(with: requestURL) { (data, response, error) in
completionHandler(data!)
}
task.resume()
}
This is how the UITableView uses it:
protocol AsyncHelper {
func getData(data: Any)
}
class TableViewController: UITableViewController, AsyncHelper {
var dataEntries: [String] = []
func getData(data: Data) {
let entry: String = String(describing: data)
dataEntries.append(entry)
}
override func viewDidLoad() {
super.viewDidLoad()
for i in 1...100 {
apiAgent.getAPIData("entry" + String(i), entry: { entry in
self.getData(data: entry)
})
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "EntryCell", for: indexPath) as! EntryCell
let entry: String = dataEntries[indexPath.row] // index out of range error
DispatchQueue.main.async {
// add Strings to cell here
}
return cell
}
}
So it appears that the cells are being generated before data gets populated into the dataEntries array. How do I prevent the UITableView from generating the cells until dataEntries is populated.
If you are going to use a closure you won't need a protocol. You could change your networking code to:
var songData = [Data]
func getAPIData(_ requestType: String, completionHandler: #escaping ([Data]) -> ()) {
let requestURL: URL = URL(string : baseURL + requestType)!
let currentSession = URLSession.shared
let task = currentSession.dataTask(with: requestURL) { (data, response, error) in
songData.append(data!)
if (songData.count == 100) {
completionHandler(songData)
}
}
task.resume()
}
This will make sure that your getData() and tableView.reloadData() will only be called once all 100 of your data elements have been loaded.
FYI - tableView.reloadData() will reload pretty much everything that has to deal with your table view. Your numberOfRows, numberOfSections, and cellForRow will all be called again. This will create the tableView over again using the updated dataEntries values
Try this :
override func viewDidLoad() {
super.viewDidLoad()
tblView.delegate = nil
tblView.dataSource = nil
for i in 1...100 {
apiAgent.getAPIData("entry" + String(i), entry: { entry in
self.getData(data: entry)
tblView.delegate = self
tblView.dataSource = self
tblView.reloadData()
})
}
}

IOS 9 TableViewCell Not Visible Until Selected

I use a service in a background thread to fetch a post request. Then I use NSJSONSerialization to turn that into an array. I loop thorough the array to create an array of teams. Then i go back to the main queue and call the completion handler.
Team:
class Team
{
private (set) var id: Int
private (set) var city: String
private (set) var name: String
private (set) var abbreviation: String
init(data: JSONDictionary)
{
id = data["team_id"] as? Int ?? 0
city = data["city"] as? String ?? ""
name = data["team_name"] as? String ?? ""
abbreviation = data["abbreviation"] as? String ?? ""
}
}
Service:
func getTeams(urlString: String, completion: [Team] -> Void)
{
let config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
let session = NSURLSession(configuration: config)
let url = NSURL(string: urlString)!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
let task = session.dataTaskWithRequest(request) {
(data, response, error) in
if error != nil {
print(error!.localizedDescription)
} else {
print(data)
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as? JSONArray {
var teams = [Team]()
for team in json {
let team = Team(data: team as! JSONDictionary)
teams.append(team)
}
let priority = DISPATCH_QUEUE_PRIORITY_HIGH
dispatch_async(dispatch_get_global_queue(priority, 0)) {
dispatch_async(dispatch_get_main_queue()) {
completion(teams)
}
}
}
} catch {
print("error in NSJSONSerialization")
}
}
}
task.resume()
}
I then try to use data to populate a tableView. I also loop through and print out all the team names to the console with success. The problem I am having It populate the tableView but everything is all white. I cant see any txt from my labels until I touch it. While the table cell is selected I can see the contents of the labels which are in black. But if i touch another one only the currently selected label is showing. It seems they should all just show up visible once the data is loaded.
custom cell:
class TeamTableViewCell: UITableViewCell {
var team: Team? {
didSet {
updateCell()
}
}
#IBOutlet weak var title: UILabel!
#IBOutlet weak var abbreviation: UILabel!
func updateCell()
{
title.text = team?.name ?? ""
abbreviation.text = team?.abbreviation ?? ""
}
}
Controller:
var teams = [Team]()
override func viewDidLoad() {
super.viewDidLoad()
title = "Teams"
let service = NBAService()
service.getTeams("https://probasketballapi.com/teams?api_key=\(Constants.API.APIKey)", completion: didLoadTeams )
}
func didLoadTeams(teams: [Team])
{
self.teams = teams
tableView.reloadData()
// This actuall works returns an list of team names to the console.
for team in teams {
print("Team: \(team.name)")
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return teams.count
}
struct Storyboard {
static let TeamCell = "TeamCell"
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(Storyboard.TeamCell, forIndexPath: indexPath) as! TeamTableViewCell
// Configure the cell...
cell.team = self.teams[indexPath.row]
return cell
}
When i print the teams names to the console that prints fine so I know that I have successfully got the data back from the request. And one team at a time is visible when the cell is selected. What am I missing
This is kind of strange:
dispatch_async(dispatch_get_global_queue(priority, 0)) {
dispatch_async(dispatch_get_main_queue()) {
completion(teams)
}
}
I would replace this with:
dispatch_async(dispatch_get_main_queue()) {
completion(teams)
}

Getting data from REST API for iOS app

This is my first time using Swift and creating an iOS app and I am having trouble retrieving data from a REST API. I am familiar with Android Development but not iOS.
I am trying to use the API from www.thecocktaildb.com.
An example of a request is http://www.thecocktaildb.com/api/json/v1/1/search.php?s=margarita.
I would like to use this request and input a string margarita, or any other drink name, from a search bar and then display the array of drinks into a tableview.
Right now when I run, I am not getting any response from the console.
Am I on the right track?
I am also not sure how to display each result (drink) in a table view cell.
Here is my file:
SearchViewController.swift
class SearchViewController: UIViewController, UISearchBarDelegate, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var TableView: UITableView!
#IBOutlet weak var SearchBar: UISearchBar!
// search in progress or not
var isSearching : Bool = false
override func viewDidLoad() {
super.viewDidLoad()
for subView in self.SearchBar.subviews
{
for subsubView in subView.subviews
{
if let textField = subsubView as? UITextField
{
textField.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("Search", comment: ""))
}
}
}
// set search bar delegate
self.SearchBar.delegate = self
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if self.SearchBar.text!.isEmpty {
// set searching false
self.isSearching = false
}else{
// set searghing true
self.isSearching = true
let postEndpoint: String = "http://www.thecocktaildb.com/api/json/v1/1/search.php?s=" + self.SearchBar.text!.lowercaseString
guard let url = NSURL(string: postEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = NSURLRequest(URL: url)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlRequest, completionHandler: { (data, response, error) in
guard let responseData = data else {
print("Error: did not receive data")
return
}
guard error == nil else {
print("error calling GET on www.thecocktaildb.com")
print(error)
return
}
// parse the result as JSON, since that's what the API provides
let post: NSDictionary
do {
post = try NSJSONSerialization.JSONObjectWithData(responseData,
options: []) as! NSDictionary
} catch {
print("error trying to convert data to JSON")
return
}
if let strDrink = post["strDrink"] as? String {
print("The drink is: " + strDrink)
}
})
task.resume()
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
// hide kwyboard when search button clicked
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
self.SearchBar.resignFirstResponder()
}
// hide keyboard when cancel button clicked
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
self.SearchBar.text = ""
self.SearchBar.resignFirstResponder()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Analizyng the json received from GET request with the provided URL http://www.thecocktaildb.com/api/json/v1/1/search.php?s=margarita
{
"drinks":[{ ... }]
}
There is a drinks key, so you should navigate to it before trying to access the deeper levels of the json. Also note that the drinks value is an array of JSON and should be cast to [NSDictionary]
The code below should help you get started with it.
if let drinks = post["drinks"] as? [NSDictionary] {
for drink in drinks {
if let strDrink = drink["strDrink"] as? String {
print("The drink is: " + strDrink)
}
}
}

Resources