append data retrieved from API for loop to array - swift

I'm having issues using a for loop to append data retrieved from an API to an array. I am attempting to use the array to dequeue cells in a tableview. I edited the question to comply with the answer provided but received errors at numberofrowsinsection (placed integer to check for more errors) and cellforrowat.
This is code in the view controller:
class BusinessesViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var businesses: [Business] = []
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
Business.searchWithTerm(term: "Restaurants", sort: .distance, categories: nil) { (businesses, error) in
self.businesses = businesses!
}
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return businesses.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Business") as! SelectTableViewCell
let business = businesses[indexPath.row]
cell.BusinessName.text = business.name
return cell
}
}
this is current console printout returned
["sort_by": distance, "open_now": 0, "term": Restaurants, "location": 37.785771,-122.406165]
Optional([<Decided.Business: 0x600000123520>, <Decided.Business: 0x600000123700>, <Decided.Business: 0x600000123c00>, <Decided.Business: 0x600000123ca0>, <Decided.Business: 0x600000123b60>, <Decided.Business: 0x604000124060>, <Decided.Business: 0x6040001241a0>, <Decided.Business: 0x604000124240>, <Decided.Business: 0x6040001244c0>, <Decided.Business: 0x604000124560>, <Decided.Business: 0x604000124600>, <Decided.Business: 0x600000123f20>, <Decided.Business: 0x6040001246a0>, <Decided.Business: 0x604000124740>, <Decided.Business: 0x6040001247e0>, <Decided.Business: 0x604000124880>, <Decided.Business: 0x600000124240>, <Decided.Business: 0x6000001242e0>, <Decided.Business: 0x600000124380>, <Decided.Business: 0x6000001244c0>])
This is the model I am using:
import UIKit
class Business: NSObject {
let name: String?
let address: String?
let imageURL: URL?
let categories: String?
let distance: String?
let ratingImage: UIImage?
let reviewCount: NSNumber?
init(dictionary: NSDictionary) {
name = dictionary["name"] as? String
let imageURLString = dictionary["image_url"] as? String
if imageURLString != nil {
imageURL = URL(string: imageURLString!)
} else {
imageURL = nil
}
let location = dictionary["location"] as? NSDictionary
var address = ""
if location != nil {
let addressArray = location!["display_address"] as? NSArray
if addressArray != nil {
if addressArray!.count > 0 {
address = addressArray![0] as! String
}
if addressArray!.count > 1 {
address += ", " + (addressArray![1] as! String)
}
}
}
self.address = address
let categoriesArray = dictionary["categories"] as? [NSDictionary]
if categoriesArray != nil {
var categoryNames = [String]()
for category in categoriesArray! {
let categoryName = category["title"] as! String
categoryNames.append(categoryName)
}
categories = categoryNames.joined(separator: ", ")
} else {
categories = nil
}
let distanceMeters = dictionary["distance"] as? NSNumber
if distanceMeters != nil {
let milesPerMeter = 0.000621371
distance = String(format: "%.2f mi", milesPerMeter * distanceMeters!.doubleValue)
} else {
distance = nil
}
let rating = dictionary["rating"] as? Double
if rating != nil {
switch rating {
case 1:
self.ratingImage = UIImage(named: "stars_1")
break
case 1.5:
self.ratingImage = UIImage(named: "stars_1half")
break
case 2:
self.ratingImage = UIImage(named: "stars_2")
break
case 2.5:
self.ratingImage = UIImage(named: "stars_2half")
break
case 3:
self.ratingImage = UIImage(named: "stars_3")
break
case 3.5:
self.ratingImage = UIImage(named: "stars_3half")
break
case 4:
self.ratingImage = UIImage(named: "stars_4")
break
case 4.5:
self.ratingImage = UIImage(named: "stars_4half")
break
case 5:
self.ratingImage = UIImage(named: "stars_5")
break
default:
self.ratingImage = UIImage(named: "stars_0")
break
}
} else {
self.ratingImage = UIImage(named: "stars_0")
}
reviewCount = dictionary["review_count"] as? NSNumber
}
class func businesses(array: [NSDictionary]) -> [Business] {
var businesses = [Business]()
for dictionary in array {
let business = Business(dictionary: dictionary)
businesses.append(business)
}
return businesses
}
class func searchWithTerm(term: String, completion: #escaping ([Business]?, Error?) -> Void) {
_ = YelpClient.sharedInstance.searchWithTerm(term, completion: completion)
}
class func searchWithTerm(term: String, sort: YelpSortMode?, categories: [String]?, completion: #escaping ([Business]?, Error?) -> Void) -> Void {
_ = YelpClient.sharedInstance.searchWithTerm(term, sort: sort, categories: categories, openNow: false, completion: completion)
}
}

Your main mistake is that you are declaring Names as a String.
After appending Hot Dog Stand the table view is creating 13 cells because the string has 13 characters which is returned in numberOfRows.
You probably want
Business.searchWithTerm(term: "Restaurants", sort: .distance, categories: nil) { (businesses, error) in
if let error = error { print(error); return }
self.businesses = businesses!
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return businesses.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Business") as! SelectTableViewCell
let business = businesses[indexPath.row]
cell.BusinessName.text = business.name
return cell
}
and delete the Names property

Related

How to Increase count of Page in Url for loading more data and show indicator at bottom?

I Creating a demo of webservices, In this I want to increase page count and load more data from api, and add in table view after activity indicator refreshing. I find many tutorials but Not found useful... They are all Advance and I'm beginner so i didn't get properly. Can Any one please tell how to do this.
Here's My Demo details...
This Is Page Count of URL
"info": {
"count": 826,
"pages": 42,
"next": "https://rickandmortyapi.com/api/character/?page=3",
"prev": "https://rickandmortyapi.com/api/character/?page=1"
},
My json Model
import UIKit
import Foundation
// MARK: - JsonModel
struct JSONModel:Decodable {
let info: Info
let results: [Result]
}
// MARK: - Info
struct Info : Decodable {
let count, pages: Int
let next: String
let prev: NSNull
}
// MARK: - Result
struct Result : Decodable {
let id: Int
let name: String
let status: Status
let species: Species
let type: String
let gender: Gender
let origin, location: Location
let image: String
let episode: [String]
let url: String
let created: String
}
enum Gender {
case female
case male
case unknown
}
// MARK: - Location
struct Location {
let name: String
let url: String
}
enum Species {
case alien
case human
}
enum Status {
case alive
case dead
case unknown
}
This is my View controller Class
import UIKit
import Kingfisher
class ViewController: UIViewController,UISearchBarDelegate{
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
var results = [Results]()
var filteredData = [Results]()
var batchSize = 42
var fromIndex = 0
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
tableView.delegate = self
tableView.dataSource = self
apiCalling()
filteredData = results
}
override func viewWillAppear(_ animated: Bool) {
filteredData = results
self.tableView.reloadData()
}
func apiCalling(){
guard let url = URL(string: "https://rickandmortyapi.com/api/character/") else { return }
URLSession.shared.dataTask(with: url) {[weak self]data, response, error in
if error != nil{
print("error While Fetching Data")
}
guard let data = data else {
return
}
do {
let resultData = try JSONDecoder().decode(JsonModel.self, from: data)
self?.results = resultData.results!
self?.filteredData = self!.results
DispatchQueue.main.async {
self?.tableView.reloadData()
}
} catch {
print(error.localizedDescription)
}
}.resume()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
let searchText = searchBar.text!
guard !searchText.isEmpty else {
filteredData = results
tableView.reloadData()
return
}
filteredData = results.filter({ $0.name!.lowercased().contains(searchText.lowercased() ) })
tableView.reloadData()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
self.searchBar.showsCancelButton = true
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.showsCancelButton = false
searchBar.text = ""
searchBar.resignFirstResponder()
filteredData.removeAll()
self.tableView.reloadData()
}
}
This My Tableview Extension
extension ViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! UserTableViewCell
let row = filteredData[indexPath.row]
let imageUrl = URL(string: row.image!)
cell.userImage.kf.setImage(with: imageUrl)
cell.lblGender.text = "Gender:- \(row.gender ?? "no value")"
cell.lblID.text = "ID:- \(row.id ?? 0)"
cell.lblName.text = "Name: \(row.name!)"
cell.lblSpecies.text = "Species:- \(row.species ?? "No Speies")"
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 250
}
}
u need save page info.
self?.info = resultData.info!
call "loadpage" when u loading more data
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
tableView.delegate = self
tableView.dataSource = self
filteredData = []
result = []
apiCalling(apiurl:"https://rickandmortyapi.com/api/character/")
}
func apiCalling(apiurl:String){
guard let url = URL(string: apiurl) else { return }
URLSession.shared.dataTask(with: url) {[weak self]data, response, error in
if error != nil{
print("error While Fetching Data")
}
guard let data = data else {
return
}
do {
let resultData = try JSONDecoder().decode(JsonModel.self, from: data)
self?.results.append(resultData.results!)
self?.info = resultData.info!
filterWord()
} catch {
print(error.localizedDescription)
}
}.resume()
}
func filterWord(){
let searchText = searchBar.text!
guard !searchText.isEmpty else {
filteredData = results
tableView.reloadData()
return
}
filteredData = results.filter({ $0.name!.lowercased().contains(searchText.lowercased() ) })
tableView.reloadData()
}
func loadPage(){
guard let page = self?.info.next,!page.isEmpty else{
return
}
apiCalling(apiurl:page)
}
under indicator simple example like this
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
guard let page = self?.info.next,!page.isEmpty else{
return nil
}
//press to call loadPage
let loading = UIButton.init()
let view = UIView.init()
view.addSubview(loading)
return view
}
I'm Giving My own Questions answer Here...
I Have Create 3 more variables
var curentIndex : Int = 0
// I'm Putting Default Limit Here...
var numberArray = Array(1...42)
var fetchingMore = false
Api Call
func apiCalling(){
guard !fetchingMore else {
print("Didn't call Get Data")
return
}
fetchingMore = true
guard let url = URL( string: "\(baseUrl)?page=\(numberArray[curentIndex])") ?? URL(string: "" ) else {
fetchingMore = false
return
}
curentIndex += 1
URLSession.shared.dataTask(with: url) {[weak self]data, response, error in
if error != nil{
print("error While Fetching Data")
}
guard let data = data else {
return
}
do {
let resultData = try JSONDecoder().decode(JsonModel.self, from: data)
self?.results += resultData.results!
self?.filteredData = self!.results
DispatchQueue.main.async {
self?.tableView.reloadData()
}
} catch {
print(error.localizedDescription)
}
self?.fetchingMore = false
}.resume()
}
**Here's My CellForRowMethod **
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! UserTableViewCell
let row = filteredData[indexPath.row]
if indexPath.row == filteredData.count - 1 && curentIndex <= row.id ?? 0 {
apiCalling()
}
let imageUrl = URL(string: row.image!)
cell.userImage.kf.setImage(with: imageUrl)
cell.lblGender.text = "Gender:- \(row.gender ?? "no value")"
cell.lblID.text = "ID:- \(row.id ?? 0)"
cell.lblName.text = "Name: \(row.name!)"
cell.lblSpecies.text = "Species:- \(row.species ?? "No Speies")"
return cell
}

Modify controller to write some data to a log file every second

I'm a Rails developer but I need to modify the controller of an application to save some sensors data to an external file.
I have this controller that take some data and populate a TableView, and data are updated every time I push a button in my view.
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: UITableView!
var currentNames: [Any] = [], voltageNames: [Any] = [], thermalNames: [Any] = []
var currentValues: [Any] = [], voltageValues: [Any] = [], thermalValues: [Any] = []
override func viewDidLoad() {
super.viewDidLoad()
currentNames = currentArray()
voltageNames = voltageArray()
thermalNames = thermalArray()
currentValues = returnCurrentValues()
voltageValues = returnVoltageValues()
thermalValues = returnThermalValues()
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch (section) {
case 0:
return currentNames.count
case 1:
return voltageNames.count
case 2:
return thermalNames.count
default:
return 0;
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "cell")!
let string: NSMutableString = ""
switch (indexPath.section) {
case 0:
// cell.textLabel.text =
let name = currentNames[indexPath.row] as! NSString
let number = currentValues[indexPath.row] as! NSNumber
string.appendFormat("%#: %.2lf", name, number.doubleValue)
case 1:
let name = voltageNames[indexPath.row] as! NSString
let number = voltageValues[indexPath.row] as! NSNumber
string.appendFormat("%#: %.2lf", name, number.doubleValue)
case 2:
let name = thermalNames[indexPath.row] as! NSString
let number = thermalValues[indexPath.row] as! NSNumber
string.appendFormat("%#: %.2lf", name, number.doubleValue)
default:
break;
}
cell.textLabel?.text = string as String?
return cell
}
func numberOfSections(in tableView: UITableView) -> Int {
return 3;
}
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
switch (section) {
case 0:
return "Current (A), \(currentNames.count) items"
case 1:
return "Voltage (V), \(voltageNames.count) items"
case 2:
return "Temperature (°C), \(thermalNames.count) items"
default:
return "";
}
}
#IBAction func reloadData(_ sender : AnyObject) {
currentValues = returnCurrentValues()
voltageValues = returnVoltageValues()
thermalValues = returnThermalValues()
tableView.reloadData()
}
}
I need to refresh data every second without pushing the button and I want to save this data in a log file.
I create a log.swift file
import Foundation
struct Log: TextOutputStream {
func write(_ string: String) {
let fm = FileManager.default
let log = fm.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("log.txt")
if let handle = try? FileHandle(forWritingTo: log) {
handle.seekToEndOfFile()
handle.write(string.data(using: .utf8)!)
handle.closeFile()
} else {
try? string.data(using: .utf8)?.write(to: log)
}
}
}
var logger = Log()
I know that I can do something every second with
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {
}
But I don't understand where to put this.
I just need to save a log with thermalNames and thermalValues
Basically the cell I have in my tableView
let name = thermalNames[indexPath.row] as! NSString
let number = thermalValues[indexPath.row] as! NSNumber
string.appendFormat("%#: %.2lf", name, number.doubleValue)
Then I will parse the file with ruby to convert to a csv...
In order to do that you have to create two functions. One for the action you want to perform and the other one for timer itself
here are the functions:
#objc func save() {
//Your Saving action has to be here
}
You have to call this one in ViewDidLoad
func startTimerForShowScrollIndicator() {
self.timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.save), userInfo: nil, repeats: true)
}

Reloading the tableview inside a ViewController getting me ' Fatal error: index out of range' , in cellforRow at index path

When I open the view it opens up quickly and doesn't show any errors but when I reopen it, it gives me 'fatal error'. This is a tableview inside the ViewController which is getting me the data but when I reopen the view it gives error.
The tableview is embedded inside the view controller when I call the tableview again it gives fatal error index out of range inside the ViewController. This needs to be solved I think issue is in my number of rows in section returning the array.
import UIKit
import Alamofire
import Foundation
protocol Quantitypass {
func qtypass(qty: String, product_id: String)
}
public struct QtyCart {
var qty : String!
public init(qty: String) {
self.qty = qty
}
}
public struct FavouriteCart {
var p_name : String!
var p_price : String!
var p_id : String!
var qty: String!
public init(p_name: String , p_price: String , p_id : String, qty: String) {
self.p_name = p_name
self.p_price = p_price
self.p_id = p_id
self.qty = qty
}
}
public struct favSection {
var favitems: [FavouriteCart]
public init(favitems: [FavouriteCart] ) {
self.favitems = favitems
}
}
public struct MyVariables {
static var product2 = "p_id"
}
public var cartData: [favSection] = []
class newCartViewController: UIViewController {
#IBAction func btnSubmit(_ sender: UIButton) {
self.loadnextVC()
}
func loadnextVC() {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "finalVC")
self.present(newViewController, animated: true, completion: nil)
}
#IBOutlet weak var lblPreorder: UILabel!
#IBOutlet weak var lblOrdermethod: UILabel!
#IBOutlet weak var lblTotalAmount: UILabel!
// public var itemsData: [Item] = []
var lblOrder = String()
// let productPrice = UserDefaults.standard.object(forKey: "p_price")
let cartid = UserDefaults.standard.object(forKey: "deviceUUID") as? String
let getApi = RestaurantAPI.self
var qtyData: [QtyCart] = []
// var currentSection = 0
// var currentRow = 1
// var calculation1 = cartTableViewCell()
#IBOutlet weak var tableView: UITableView!
#IBAction func dismiss(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
self.lblTotalAmount.text = ""
}
//public var cartData: [FavouriteCart] = []
var refreshControl = UIRefreshControl()
var buttonClicked = statusViewController.self
var btn2 = UserDefaults.standard.object(forKey:"button1") as? Bool
var btn3: Bool = true
weak var delegate: DetailsDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.getTotal1()
print("#------/\(cartData.count)")
//self.updateLabel(withString: lblTotalAmount.text)
if btn2 != btn3 {
lblPreorder.isHidden = true
lblOrdermethod.isHidden = false
print("BUTTONCLICKED")
UserDefaults.standard.removeObject(forKey: "button1")
} else if btn2 == btn3 {
lblPreorder.isHidden = false
lblOrdermethod.isHidden = true
print("BUTTON-NOT-CLICKED")
UserDefaults.standard.removeObject(forKey: "button1")
}
// if #available(iOS 10.0, *) {
// tableView.refreshControl = refreshControl
// } else {
// tableView.addSubview(refreshControl)
//
// }
// self.refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
// self.refreshControl.addTarget(self, action: #selector(newCartViewController.refreshData), for: UIControlEvents.valueChanged)
//
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.view.addSubview(self.tableView)
self.tableView.dataSource = self
self.tableView.delegate = self
self.getTotal1()
self.getFav()
self.tableView.reloadData()
// self.updateTableview()
// self.getTotal1()
}
func getFav() {
getFav(completionHandler: { success in
if success {
DispatchQueue.main.async{
self.tableView.reloadData()
}
}
})
}
//TOTAL API CALL:
func getTotal1() {
if cartid != nil {
let request = getApi.getamountcartGetWithRequestBuilder(restId: "17", cartId: cartid!)
Alamofire.request(request.URLString, method: .get , parameters: nil, encoding: JSONEncoding.default)
.responseJSON { response in
print("123321#######/\(response)")
let res = response
print("101res/\(res)")
if let value = response.value as? [String: AnyObject] {
if let success = value["error"] as? Bool {
if success == false {
// var grandtotal: Any? = value["total"]
if let grandtotal = value["total"] as? Double {
self.lblTotalAmount.text = String(grandtotal)
print("!#/\(String(grandtotal))")
var g = String(grandtotal)
UserDefaults.standard.set(g, forKey: "g")
}
// self.tableView.reloadData()
}
}
}
}
}
}
//
// Mark: getting all cart items:-->
func getFav(completionHandler: #escaping (Bool) -> Void){
if cartid != nil {
let request = getApi.displaycartGetWithRequestBuilder(restId: "17", cartId:cartid!)
Alamofire.request(request.URLString, method: .get , parameters: nil, encoding: JSONEncoding.default)
.responseJSON { response in
print("123321/\(response)")
let res = response
print("101res/\(res)")
// let total = cartData
// print("cartd1/\(total)")
if let value = response.value as? [String: AnyObject] {
if let success = value["error"] as? Bool {
if success == false {
print("2222/\(response)")
if let response = value["cartdata"] as? [String: AnyObject] {
let cart = response["simple"] as! [[String: AnyObject]]
// let total = value["total"] as! [String: Any]
// print("1231231231234/\(total)")
//let userdata: [Favourites] = []
//
var cartitems: [FavouriteCart] = []
for (_ , value) in cart.enumerated() {
let obj = value
let p_name = obj["p_name"] as! String
let p_price = obj["p_price"] as! String
let p_id = obj["p_id"] as! String
let qty = obj["qty"] as! String
// UserDefaults.standard.set(qty, forKey: "qty")
let item = FavouriteCart(p_name: p_name, p_price: p_price, p_id: p_id, qty: qty)
cartitems.append(item)
DispatchQueue.main.async{
print("RELOADED-----ON-----API")
cartData.append(favSection(favitems: cartitems))
self.tableView.reloadData()
}
}
}
print("COMPLETION-------------HANDLER")
completionHandler(true)
}
}
else
{
let myAlert = UIAlertController(title:"Alert",message:value["error_msg"] as? String,preferredStyle:UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title:"OK",style:UIAlertActionStyle.default , handler: nil)
myAlert.addAction(okAction)
}
}
}
}
}
// Mark:--> Delete items from cart.
// func delFav(p_id: String ,completionHandler: #escaping (Bool) -> Void) {
func delFav(p_id: String, completionHandler: #escaping (Bool) -> Void){
//var product1 = UserDefaults.standard.object(forKey: "p_id")
//print("this is my \(product1)")
let request = getApi.deleteproductcartGetWithRequestBuilder(restId: "17", cartId: cartid!, productId: p_id , freeDish: "none", type: "simple")
Alamofire.request(request.URLString, method: .delete , parameters: nil, encoding: JSONEncoding.default)
.responseJSON { response in
print("del favvvvvvvvv\(response)")
print("<<DELETED ------------ ITEM>>")
//print(response)
if let value = response.value as? [String: AnyObject] {
if let success = value["error"] as? Bool {
if success == false {
let response = value["cartdata"] as? [String]
print("10001 - > /\(response)")
//self.tableView.reloadData()
} else
{
print("error message")
}
}
}
// completionHandler(true)
}
}
func delterow(p_id: String) {
self.delFav(p_id: p_id, completionHandler: {sucess in
if sucess {
self.tableView.reloadData()
print("DELETED_ITEM")
}
})
}
func updateTotal(withString string: String?) {
lblTotalAmount.text = string
}
}
extension newCartViewController: UITableViewDelegate , UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 90;
}
// func numberOfSections(in tableView: UITableView) -> Int {
// return cartData.count
// }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return sectionsData[section].items.count
// return cartData[section].favitems.count
// return cartData[section]..count
return cartData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cartTablecell", for: indexPath) as! cartTableViewCell
let item: FavouriteCart = cartData[indexPath.section].favitems[indexPath.row]
cell.btnsub1.tag = indexPath.row
cell.btnadd1.tag = indexPath.row
cell.lblItemName.text = item.p_name
cell.productPrice.text = item.p_price
cell.lblprice.text = item.p_price
cell.lblQuantity.text = item.qty
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool{
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
if cartData.isEmpty{
print("EmptyCart")
}else {
print("0-0/\(cartData.count)")
// let item: Item = sectionsData[indexPath.section].items[indexPath.row]
let item: FavouriteCart = cartData[indexPath.section].favitems[indexPath.row]
self.tableView.beginUpdates()
cartData.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .fade)
self.delterow(p_id: item.p_id)
self.getTotal1()
self.tableView.endUpdates()
}
}
}
}
It was giving the error because the rows were saved at local but I was calling the data again changes are :
have to delete the whole cart when close the view
#IBAction func dismiss(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
self.lblTotalAmount.text = ""
cartData.removeAll()
}
I think self.tableView.dataSource = self & self.tableView.delegate = self should be in viewDidLoad method. Adding it to viewDidLoad might work.

Search Bar Swift 3 - Can't use in/contains operator with collection

I am implementing a search bar into my project but I am being presented with the below error.
reason: 'Can't use in/contains operator with collection wellpleased.attendees.UserData(firstname: "Ben", lastname: "Delonge", fullname: "Ben Delonge", company: "AllStar.com", jobtitle: "Business Development Manager", image: "6.jpg") (not a collection)'
I have done plenty of searching around the NSPredicate but cannot seem to prevent this crashing.
I am using the code below, any assistance resolving this would be much appreciated.
class attendees: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
var tableData = ""
var value:String!
var searchString: String = ""
var dataSource: [UserData] = []
struct UserData {
var firstname: String
var lastname: String
var fullname: String
var company: String
var jobtitle: String
var image: String
}
var filteredAppleProducts = [String]()
var resultSearchController = UISearchController()
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
print(value)
searchBar.delegate = self
self.tableView.reloadData()
let nib = UINib(nibName: "vwTblCell2", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "cell2")
}
override func viewDidAppear(_ animated: Bool) {
getTableData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if filteredAppleProducts != []{
return self.filteredAppleProducts.count
}
else
{
if searchString != "[]" {
return self.dataSource.count
}else {
return 0
}
}
}
// 3
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell2: TblCell2 = self.tableView.dequeueReusableCell(withIdentifier: "cell2") as! TblCell2
print(filteredAppleProducts)
if filteredAppleProducts != []{
cell2.nameLabel.text = self.filteredAppleProducts[indexPath.row]
return cell2
}
else
{
if searchString != "[]"{
cell2.nameLabel.text = self.dataSource[indexPath.row].fullname
cell2.companyLabel.text = self.dataSource[indexPath.row].company
cell2.jobTitleLabel.text = self.dataSource[indexPath.row].jobtitle
let url = URL(string: "https://www.asmserver.co.uk/wellpleased/backend/profileimages/\(self.dataSource[indexPath.row].image)")
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
cell2.userImage.image = UIImage(data: data!)
}
return cell2
}
}
// 4
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
// 5
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 90
}
func updateSearchResults(){
self.filteredAppleProducts.removeAll(keepingCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchString)
let array = (self.dataSource as NSArray).filtered(using: searchPredicate)
self.filteredAppleProducts = array as! [String]
self.tableView.reloadData()
print(filteredAppleProducts)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print("searchText \(searchText)")
print(filteredAppleProducts)
searchString = searchText
updateSearchResults()
}
func getTableData(){
self.dataSource.removeAll()
let defaults = UserDefaults()
let userid = defaults.string(forKey: "id")
let url = NSURL(string: "https://www.******.co.uk/wellpleased/backend/searchattendees.php?userid=\(userid!)&eventid=\(value!)")
print(url)
let task = URLSession.shared.dataTask(with: url as! URL) { (data, response, error) -> Void in
if let urlContent = data {
do {
if let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: []) as? [[String:AnyObject]] {
var i = 0
while i < jsonResult.count {
self.dataSource.append(UserData(firstname:"\(jsonResult[i]["firstname"]! as! String)", lastname: "\(jsonResult[i]["lastname"]! as! String)", fullname:"\(jsonResult[i]["fullname"]! as! String)", company: "\(jsonResult[i]["company"]! as! String)", jobtitle:"\(jsonResult[i]["jobtitle"]! as! String)", image:"\(jsonResult[i]["image"]! as! String)"))
i = i + 1
}
}
} catch {
print("JSON serialization failed")
}
} else {
print("ERROR FOUND HERE")
}
DispatchQueue.main.async(execute: { () -> Void in
self.tableView.reloadData()
})
self.tableView.isUserInteractionEnabled = true
}
task.resume()
}
}
I have also tired:
let searchPredicate = NSPredicate(format: "fullname CONTAINS[c] %#", searchString as String)
which returns the error:
this class is not key value coding-compliant for the key fullname
NSPredicate is a Cocoa feature that lives in the Objective-C world. It's never going to work on an array of UserData because UserData is a Swift struct — and Objective-C cannot see a Swift struct at all (and even if it could, it certainly can't see any type namespaced inside a class, as your UserData is).
You would have an easy time of this if you simply used the built-in Swift filter method to filter the dataSource array. For example (if this is what you're trying to do):
let array = self.dataSource.filter{$0.fullname.contains(searchString)}
In Swift 3, you can combine NSArray with NSPredicate like this:
let searchPredicate = NSPredicate(format: "%K CONTAINS[c] %#", "fullname",searchString)
let array = NSArray(array: self.dataSource).filtered(using: searchPredicate)

Swift 3 table search

I have populated a table using json data from a remote server.
I am now trying to add a search bar which will filter the results.
The issue I am facing is that I am storing the json data in several arrays.
name, company, job title etc
This means that when the user searches only the name array is filtered and displayed in the table correctly, the other information is out of sync as it remains unfiltered.
Am I approaching this in the correct way?
class attendees: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
var tableData = ""
var value:String!
var searchString = ""
var firstname: [String] = []
var lastname: [String] = []
var fullname: [String] = []
var company: [String] = []
var jobtitle: [String] = []
var image: [String] = []
var filteredAppleProducts = [String]()
var resultSearchController = UISearchController()
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
print(value)
searchBar.delegate = self
self.tableView.reloadData()
let nib = UINib(nibName: "vwTblCell2", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "cell2")
}
override func viewDidAppear(_ animated: Bool) {
getTableData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if filteredAppleProducts != []{
return self.filteredAppleProducts.count
}
else
{
if searchString != "[]" {
return self.firstname.count
}else {
return 0
}
}
}
// 3
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell2: TblCell2 = self.tableView.dequeueReusableCell(withIdentifier: "cell2") as! TblCell2
print(filteredAppleProducts)
if filteredAppleProducts != []{
cell2.nameLabel.text = self.filteredAppleProducts[indexPath.row]
return cell2
}
else
{
if searchString != "[]"{
cell2.nameLabel.text = "\(self.firstname[indexPath.row]) \(self.lastname[indexPath.row])"
cell2.companyLabel.text = self.company[indexPath.row]
cell2.jobTitleLabel.text = self.jobtitle[indexPath.row]
let url = URL(string: "https://www.asmserver.co.uk/wellpleased/backend/profileimages/\(self.image[indexPath.row])")
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
cell2.userImage.image = UIImage(data: data!)
}
return cell2
}
}
// 4
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
// 5
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 90
}
func updateSearchResults(){
self.filteredAppleProducts.removeAll(keepingCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchString)
let array = (self.fullname as NSArray).filtered(using: searchPredicate)
self.filteredAppleProducts = array as! [String]
self.tableView.reloadData()
print(filteredAppleProducts)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print("searchText \(searchText)")
print(filteredAppleProducts)
searchString = searchText
updateSearchResults()
}
func getTableData(){
self.firstname.removeAll()
self.lastname.removeAll()
self.fullname.removeAll()
self.company.removeAll()
self.jobtitle.removeAll()
self.image.removeAll()
let defaults = UserDefaults()
let userid = defaults.string(forKey: "id")
let url = NSURL(string: "https://www.asmserver.co.uk/wellpleased/backend/searchattendees.php?userid=\(userid!)&eventid=\(value!)")
print(url)
let task = URLSession.shared.dataTask(with: url as! URL) { (data, response, error) -> Void in
if let urlContent = data {
do {
if let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: []) as? [[String:AnyObject]] {
var i = 0
while i < jsonResult.count {
self.firstname.append(jsonResult[i]["firstname"]! as! String)
self.lastname.append(jsonResult[i]["lastname"]! as! String)
let fname = jsonResult[i]["firstname"]! as! String
let lname = jsonResult[i]["lastname"]! as! String
let fullname1 = "\(fname) \(lname)"
self.fullname.append(fullname1)
self.company.append(jsonResult[i]["company"]! as! String)
self.jobtitle.append(jsonResult[i]["jobtitle"]! as! String)
self.image.append(jsonResult[i]["image"]! as! String)
i = i + 1
}
}
} catch {
print("JSON serialization failed")
}
} else {
print("ERROR FOUND HERE")
}
DispatchQueue.main.async(execute: { () -> Void in
self.tableView.reloadData()
})
self.tableView.isUserInteractionEnabled = true
}
task.resume()
}
}
Use a struct or class for the data. This way it'll be easier to keep track of the data, and in any case it looks like you don't have any good reason to keep track of seven different arrays.
As an example:
struct Data {
var firstName: String
var lastName: String
var fullName: String
var company: String
var jobTitle: String
var image: String
}
And populate just the one array:
var dataSource: [Data] = []
Access with property name, instead of arrayName[index]:
let name = dataSource[index].firstName