Display List of data when button is clicked - arrays

I have 2 different JSON and I want to display list of data when the image from the collectionview is pressed to my another view controller.
Those list of data should be clickable also.
It would be one SkillsName(TSCSkillName) to many Training's (TSCTtopicName)
SAMPLE JSON
{
"TSCskillID": 1,
"TSCProficiency": "Product",
"TSCLevel": "Fundamental",
"TSCSkillName": "Product Usability",
"TSCskillLOGO": "images\/TSCskillLogo\/Product Usability.jpg",
"TSCskillDescription": "SKILLS Test Description"
}
For the second JSON
{
"TSCProficiency": "Product",
"TSCSkillName": "Product Usability",
"TSCskillDescription": "SKILLS Test Description",
"TSCLevel": "Fundamental",
"TSCskillLOGO": "images\/TSCskillLogo\/Product Usability.jpg",
"TSCTtopicID": 1,
"TSCTtopicName": "Trend Micro Security",
"TSCTtopicDescription": "TOPIC Test Description",
"Status": "Done",
"TSCTMemorabilia": "images\/Memorabilia"
},
{
"TSCProficiency": "Product",
"TSCSkillName": "Product Usability",
"TSCskillDescription": "SKILLS Test Description",
"TSCLevel": "Fundamental",
"TSCskillLOGO": "images\/TSCskillLogo\/Product Usability.jpg",
"TSCTtopicID": 2,
"TSCTtopicName": "Trend Micro Antivirus for Mac",
"TSCTtopicDescription": "TOPIC Test Description",
"Status": "Done",
"TSCTMemorabilia": "images\/Memorabilia"
}
// I used this data to display the images to my collectionview
struct getSkills: Codable {
let TSCProficiency, TSCSkillName, TSCskillDescription, TSCLevel: String
let TSCskillLOGO: String
let TSCTtopicID: Int?
let TSCTtopicName, TSCTtopicDescription, status, TSCTMemorabilia: String
enum CodingKeys: String, CodingKey {
case TSCProficiency = "TSCProficiency"
case TSCSkillName = "TSCSkillName"
case TSCskillDescription = "TSCskillDescription"
case TSCLevel = "TSCLevel"
case TSCskillLOGO = "TSCskillLOGO"
case TSCTtopicID = "TSCTtopicID"
case TSCTtopicName = "TSCTtopicName"
case TSCTtopicDescription = "TSCTtopicDescription"
case status = "Status"
case TSCTMemorabilia = "TSCTMemorabilia"
}
}
struct skills {
static var TSCskillLOGO : String = "Photo Location"
static var TSCskillID: String = "1"
static var TSCProficiency: String = "Skill Proficiency"
static var TSCSkillName: String = "Skill Name"
static var TSCskillDescription: String = "Skill Description"
static var TSCLevel: String = "Skill Level"
static var TSCTtopicID: String = "Trainings ID"
static var TSCTtopicName: String = "Training Name"
static var TSCTtopicDescription: String = "Training Description"
static var Status: String = "Status"
static var TSCTMemorabilia: String = "Memorabilia"
}
struct testSkills: Codable {
let TSCskillID: Int
let TSCProficiency, TSCLevel,TSCSkillName, TSCskillLOGO: String
let TSCskillDescription: String
enum CodingKeys: String, CodingKey {
case TSCskillID = "TSCskillID"
case TSCProficiency = "TSCProficiency"
case TSCLevel = "TSCLevel"
case TSCSkillName = "TSCSkillName"
case TSCskillLOGO = "TSCskillLOGO"
case TSCskillDescription = "TSCskillDescription"
}
}
class SkillsTreeViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var skillsCollectionView: UICollectionView!
#IBOutlet weak var skillsCatLbl: UILabel!
var getSkillsInfo = [getSkills]()
var testSkillsInfo = [testSkills]()
var badges: [UIImage] = []
var isBadgeLoaded = false
var retryTimes = 0
// You may check my code below
override func viewDidLoad() {
super.viewDidLoad()
skillsCollectionView.layer.cornerRadius = 10
skillsCollectionView.layer.masksToBounds = true
skillsCatLbl.text = "Network"
loadData()
}
func loadData() {
handleCollectionViewLayout(collectionView: skillsCollectionView)
getTestSkillsList {
self.addValuesToVariables {
self.handleLoading(view: self.skillsCollectionView, isDoneLoading: !self.isBadgeLoaded)
self.loadImages()
}
}
// getSkillsDetails {}
}
func addValuesToVariables(completed: #escaping () -> ()) {
skills.TSCskillLOGO = testSkillsInfo[0].TSCskillLOGO
skills.TSCProficiency = testSkillsInfo[0].TSCProficiency
skills.TSCSkillName = testSkillsInfo[0].TSCSkillName
skills.TSCskillDescription = testSkillsInfo[0].TSCskillDescription
skills.TSCLevel = testSkillsInfo[0].TSCLevel
skills.TSCskillID = String(testSkillsInfo[0].TSCskillID)
completed()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// return getSkillsInfo.count
return testSkillsInfo.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let collectionviewcell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionviewcell", for: indexPath) as! FAMECell
collectionviewcell.skillsBadgePic?.addImageShadow(shadowOffset: CGSize(width: 0, height: 0), shadowColor: UIColor.black.cgColor, shadowRadius: 0.5, shadowOpacity: 5.0)
collectionviewcell.skillsBadgePic?.contentMode = .scaleAspectFit
if isBadgeLoaded {
activityIndicator.stopAnimating()
collectionviewcell.skillsBadgePic?.image = badges[indexPath.item]
}
return collectionviewcell
//collectionviewcell.skillsBadgePic?.image = getSkillsInfo[indexPath.item].image
//}
// print(getSkillsInfo[indexPath.item].image!)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("User Tapped: \(indexPath.item)")
let selectedCell = skillsCollectionView.cellForItem(at: indexPath) as! FAMECell
let mainStoryboard:UIStoryboard = UIStoryboard (name: "Main", bundle:nil)
let desVC = mainStoryboard.instantiateViewController(withIdentifier: "SkillsTreePopUpViewController") as! SkillsTreePopUpViewController
desVC.badgeTitle = testSkillsInfo[indexPath.item].TSCSkillName
desVC.badgeImage = selectedCell.skillsBadgePic?.image
desVC.badgeDescription = testSkillsInfo[indexPath.item].TSCskillDescription
desVC.skillID = testSkillsInfo[indexPath.item].TSCskillID
desVC.trainingsBtn = getSkillsInfo[indexPath.item].TSCTtopicName //getting an error on this line [array is nil]
self.present(desVC, animated: true, completion: nil)
}
func getTestSkillsList(completed: #escaping () -> ()) {
let fcat = self.skillsCatLbl.text
let siebelid = engineerProfileInfo.siebelID
let rootLink = "https://skills.dasq.com/iOS/testskills.php?"
let url = URL (string: rootLink + "id=" + siebelid + "&fcat=" + fcat!)
URLSession.shared.dataTask(with: url!) {(data, response, error) in
if error == nil {
do {
self.testSkillsInfo = try JSONDecoder().decode([testSkills].self, from: data!)
print(self.testSkillsInfo)
DispatchQueue.main.async {
completed()
}
} catch {
print("JSON Error: Skills Tree")
self.handleJSONErrorAlert()
}
} else {
self.handleNoNetAlert()
}
}.resume()
}
func getSkillsDetails(completed: #escaping () -> ()) {
let tid = skills.TSCskillID
let siebelid = engineerProfileInfo.siebelID
let rootLink = "https://skills.dasq.com/iOS/getskillspage.php"
let url = URL (string: rootLink + "sid=" + siebelid + "&tid=" + tid )
URLSession.shared.dataTask(with: url!) {(data, response, error) in
if error == nil { // getting an error here that array is nil
do {
self.getSkillsInfo = try JSONDecoder().decode([getSkills].self, from: data!)
DispatchQueue.main.async {
completed()
}
} catch {
print("JSON Error: Trainings Details")
print (error)
self.handleJSONErrorAlert()
}
} else {
self.handleNoNetAlert()
}
}.resume()
}
func loadImages(){
for index in 1...testSkillsInfo.count {
let badgeTitle = testSkillsInfo[index-1].TSCSkillName
let completeLink = "https://skills.dasq.com/" + "images/TSCskillLogo/\(badgeTitle).jpg"
let imageUrlString = completeLink.addingPercentEncoding( withAllowedCharacters: .urlQueryAllowed)
let url = URL(string: imageUrlString!)
let data = try? Data(contentsOf: url!)
let coloredPic = UIImage(data: data!)
badges.append(coloredPic!)
}
isBadgeLoaded = !isBadgeLoaded
print(badges.count)
self.skillsCollectionView.reloadData()
}

Please check you decodable struct. I have generated one for you. The error shows that
JSON text did not start with array.
So, try to decode "TopicName" which is an array of "TopicNameElement"
SkillName
struct SkillName: Codable {
let tsCskillID: Int
let tscProficiency, tscLevel, tscSkillName, tsCskillLOGO: String
let tsCskillDescription: String
enum CodingKeys: String, CodingKey {
case tsCskillID = "TSCskillID"
case tscProficiency = "TSCProficiency"
case tscLevel = "TSCLevel"
case tscSkillName = "TSCSkillName"
case tsCskillLOGO = "TSCskillLOGO"
case tsCskillDescription = "TSCskillDescription"
}
}
TopicName
typealias TopicName = [TopicNameElement]
struct TopicNameElement: Codable {
let tscProficiency, tscSkillName, tsCskillDescription, tscLevel: String
let tsCskillLOGO: String
let tscTtopicID: Int
let tscTtopicName, tscTtopicDescription, status, tsctMemorabilia: String
enum CodingKeys: String, CodingKey {
case tscProficiency = "TSCProficiency"
case tscSkillName = "TSCSkillName"
case tsCskillDescription = "TSCskillDescription"
case tscLevel = "TSCLevel"
case tsCskillLOGO = "TSCskillLOGO"
case tscTtopicID = "TSCTtopicID"
case tscTtopicName = "TSCTtopicName"
case tscTtopicDescription = "TSCTtopicDescription"
case status = "Status"
case tsctMemorabilia = "TSCTMemorabilia"
}
}

Related

create a function that finds the closest object base on Lat long in an array

I have created the following code for getting the closest point in an array to a users location. This is not working. I can get locations but not closest. My goal is to be able to gat the closest point and print out the pm2 values of the item.
import UIKit
import CoreLocation
extension Point {
var location: CLLocation { .init(latitude: latitude ?? 5, longitude: longitude ?? 5) }
}
class popAirViewController: UITableViewController, CLLocationManagerDelegate {
private let popAirURL = "https://www.purpleair.com/json"
private var results = [Point]()
var locationManager: CLLocationManager!
var PM5Value: Int = 5
var points: [Point] = []
var destination: CLLocation?
//Temporary background image
var backgroundImage: String = "Green"
// sets PM5 text Label
#IBOutlet weak var latLabel: UILabel!
//#IBOutlet weak var latLabel: UILabel!
func PM5Text(){
latLabel.text = "56.5"
latLabel.textAlignment = .center
latLabel.numberOfLines = 10
latLabel.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height+300)
}
override func viewDidLoad() {
self.tableView.backgroundView = UIImageView(image: UIImage(named: backgroundImage))
self.tableView.backgroundColor = .clear
super.viewDidLoad()
//sets background images
if 0 ... 50 ~= Int(PM5Value) {
self.tableView.backgroundView = UIImageView(image: UIImage(named: "Green"))}
else if 51 ... 100 ~= Int(PM5Value) {
self.tableView.backgroundView = UIImageView(image: UIImage(named: "Yellow"))}
else if 101 ... 150 ~= Int(PM5Value) {
self.tableView.backgroundView = UIImageView(image: UIImage(named: "Orange"))}
else if 151 ... 1000 ~= Int(PM5Value) {
self.tableView.backgroundView = UIImageView(image: UIImage(named: "Purple"))}
print(PM5Value)
self.navigationController?.navigationBar.isHidden = true;
//let fullImageView = UIImageView(frame: self.view.frame)
//view.addSubview(fullImageView)
if (CLLocationManager.locationServicesEnabled())
{
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
tableView.estimatedRowHeight = 92.0
tableView.rowHeight = UITableView.automaticDimension
getLatestLocations()
}
// MARK: - Table view data source
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
PM5Text()
}
override func numberOfSections(in tableView: UITableView) -> Int {
// Return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows
return results.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! KivaLoanTableViewCell
// Configure the cell...
cell.amountLabel.text = results[indexPath.row].pm2
return cell
}
// MARK: - Helper methods
func getLatestLocations() {
guard let pointUrl = URL(string: popAirURL) else {
return
}
let request = URLRequest(url: pointUrl)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
if let error = error {
print(error)
return
}
// Parse JSON data
if let data = data {
self.results = self.parseJsonData(data: data)
// Reload table view
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
})
task.resume()
}
func parseJsonData(data: Data) -> [Point] {
var points = [Point]()
do {
let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
// Parse JSON data
let jsonPoints = jsonResult?["results"] as! [AnyObject] as [AnyObject]
for jsonPoint in jsonPoints {
var point = Point()
point.latitude = jsonPoint["Lat"] as? Double
point.longitude = jsonPoint["Lon"] as? Double
point.pm2 = jsonPoint["PM2_5Value"] as? String
let latq = point.latitude
let lonq = point.longitude
//let destination = CLLocationCoordinate2DMake(latq ?? 5, lonq ?? 5)
self.destination = CLLocation(latitude: latq ?? 5, longitude: lonq ?? 5)
points.append(point)
//print(destination)
print("Destination=: \(destination!)")
}
} catch {
print(error)
}
return points
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
var userLocation:CLLocation = locations[0]
let long = userLocation.coordinate.longitude;
let lat = userLocation.coordinate.latitude;
if let closestPoint = points.min(by:{
$0.location.distance(from: userLocation) < $1.location.distance(from: userLocation)
}) {
print("closest point:", closestPoint)
}
if destination == nil{
return
}
userLocation = CLLocation(latitude: lat , longitude: long )
}
}
You can use CLLocation distance(from: CLLocation) method to get the distance from the current location and use min(by: ) method to get the minimum distance from your points:
var points: [Point] = []
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
if let closestPoint = points.min(by: {
$0.location.distance(from: location) < $1.location.distance(from: location)
}) {
print("closest point:", closestPoint)
}
}
Add this helper to construct a location from your point:
extension Point {
var location: CLLocation { .init(latitude: latitude, longitude: longitude) }
}
Btw it is Swift naming convention to name your properties starting with a lower case letter:
struct Point: Equatable {
let pm2: String
let latitude: Double
let longitude: Double
}
edit/update: Not related to your question but you should use Codable protocol to parse the json response from the API:
Playground testing:
import UIKit
import CoreLocation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
struct Root: Codable {
let results: [Point]
}
struct Point: Codable {
let pm2: String?
let latitude, longitude: Double?
enum CodingKeys: String, CodingKey {
case pm2 = "PM2_5Value", latitude = "Lat", longitude = "Lon"
}
}
extension Point {
var location: CLLocation { .init(latitude: latitude ?? .zero, longitude: longitude ?? .zero) }
}
let popAirURL = "https://www.purpleair.com/json"
URLSession.shared.dataTask(with: URL(string: popAirURL)!) { data, response, error in
guard let data = data else {
print("error", error!)
return
}
do {
let points = try JSONDecoder().decode(Root.self, from: data).results
let location = CLLocation(latitude: 38, longitude: -121)
if let closestPoint = points.min(by: {
$0.location.distance(from: location) < $1.location.distance(from: location)
}) {
print("closest point:", closestPoint) // closest point: Point(pm2: Optional("107.47"), latitude: Optional(38.100134), longitude: Optional(-120.860454))
}
} catch {
print(error)
}
}.resume()

for loop only shows first item in array swift

I have a function called loadPosts that returns an array of Int values. Upon running it is used within the UITableView which has a function called setCell. Only the first item in the array is being used and then it repeats that value for the length of the array.
UPDATE 2:
Here are the parameters within the hhmessages array:
1. senderusername
2. recipient
3. message text
4. ava image
UPDATED: now includes additional code in loadPosts function
func loadPosts()->[Int] {
let me = user!["username"] as! String
let uuid = messages["uuid"] as! String
let url = URL(string: "http://localhost/message.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let body = "username=\(me)&uuid=\(uuid)"
request.httpBody = body.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
if error == nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
self.hhmessages.removeAll(keepingCapacity: false)
self.tableView.reloadData()
// declare new parseJSON to store json
guard let parseJSON = json else {
print("Error while parsing")
return
}
guard let messages = parseJSON["messages"] as? [AnyObject] else {
print("Error while parseJSONing")
return
}
self.hhmessages = messages
//print(self.hhmessages)
for i in 0 ..< self.hhmessages.count {
if me == self.hhmessages[i]["senderusername"]!! as! String {
self.incoming = [0]
}
if me == self.hhmessages[i]["recipient"]!! as! String {
self.incoming = [1]
}
}
self.tableView.reloadData()
return [Int()]
}
// UITableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ConversationCell
func setCell(incoming: [Int]) {
var layoutAttribute: NSLayoutAttribute
var layoutConstant: CGFloat
for i in 0 ..< self.incoming.count {
if (self.incoming[i] == 1) {
cell.bubbleImageView.image=#imageLiteral(resourceName: "chat_bubble_received")
cell.messageLbl.textColor = UIColor.black
layoutAttribute = .left
layoutConstant = 10
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.bubbleImageView, attribute: layoutAttribute, relatedBy: .equal, toItem: cell.contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
}
if (self.incoming[i] == 0) {
cell.bubbleImageView.image = #imageLiteral(resourceName: "chat_bubble_sent")
cell.messageLbl.textColor = UIColor.white
layoutAttribute = .right
layoutConstant = -10
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.bubbleImageView, attribute: layoutAttribute, relatedBy: .equal, toItem: cell.contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
}
}
}
// get main queue to this block of code to communicate back
DispatchQueue.main.async {
tableView.transform = CGAffineTransform(rotationAngle: -CGFloat.pi)
cell.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
setCell(incoming: self.incoming)
}
return cell
}
func loadPosts()->[Int] {
let me = user!["username"] as! String
let uuid = messages["uuid"] as! String
let url = URL(string: "http://localhost/message.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let body = "username=\(me)&uuid=\(uuid)"
request.httpBody = body.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
if error == nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
self.hhmessages.removeAll(keepingCapacity: false)
self.tableView.reloadData()
// declare new parseJSON to store json
guard let parseJSON = json else {
print("Error while parsing")
return
}
guard let messages = parseJSON["messages"] as? [AnyObject] else {
print("Error while parseJSONing")
return
}
self.hhmessages = messages
//print(self.hhmessages)
/// This is the part I edited
for i in 0 ..< self.hhmessages.count {
if me == self.hhmessages[i]["senderusername"]!! as! String {
self.incoming.append(0)
}
if me == self.hhmessages[i]["recipient"]!! as! String {
self.incoming.append(1)
}
}
self.tableView.reloadData()
return [Int()]
}
Change your cellForRowAt to use the indexPath.row as the index for self.incoming :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ConversationCell
func setCell(incoming: [Int]) {
var layoutAttribute: NSLayoutAttribute
var layoutConstant: CGFloat
if (self.incoming[indexPath.row] == 1) {
cell.bubbleImageView.image=#imageLiteral(resourceName: "chat_bubble_received")
cell.messageLbl.textColor = UIColor.black
layoutAttribute = .left
layoutConstant = 10
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.bubbleImageView, attribute: layoutAttribute, relatedBy: .equal, toItem: cell.contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
}
if (self.incoming[indexPath.row] == 0) {
cell.bubbleImageView.image = #imageLiteral(resourceName: "chat_bubble_sent")
cell.messageLbl.textColor = UIColor.white
layoutAttribute = .right
layoutConstant = -10
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.bubbleImageView, attribute: layoutAttribute, relatedBy: .equal, toItem: cell.contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
}
}
// get main queue to this block of code to communicate back
DispatchQueue.main.async {
tableView.transform = CGAffineTransform(rotationAngle: -CGFloat.pi)
cell.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
setCell(incoming: self.incoming)
}
return cell
}

Swift 3 JSON to array of objects is very slow

I am upgrading my app from swift 2 to swift 3 and have a significant performance loss when building an array of custom objects. In swift 2 this took a few seconds while in swift 3 it takes around 30 seconds. I am using alamofire to return swiftyJSON, returning about 3000 rows of data. the alamofire return is quick, its looping through this json to build array of custom objects thats slow. These objects greatly simplify the code written when building table cells and passing data to new views.
Code:
class Customer {
var ID: String!
var sysName: String!
var address: String!
var contactID: String!
required init(_name:String?, _id: String?, _address:String?, _contactID:String?) {
//print(json)
if _id != nil {
self.ID = _id
}else{
self.ID = ""
}
if _name != nil {
self.sysName = _name
}else{
self.sysName = ""
}
if _address != nil {
self.address = _address
}else{
self.address = "No Address on File"
}
if _contactID != nil {
self.contactID = _contactID
}else{
self.contactID = ""
}
}
}
Alamofire.request(API.Router.customerList()).responseJSON() {
response in
print(response.request ?? "") // original URL request
print(response.response ?? "") // URL response
print(response.data ?? "") // server data
print(response.result) // result of response serialization
if let json = response.result.value {
print("JSON: \(json)")
self.customers = JSON(json)
self.parseJSON()
}
}
func parseJSON(){
let jsonCount = self.customers["customers"].count
self.totalCustomers = jsonCount
for i in 0 ..< jsonCount {
self.loadedCustomers = i
print("customer = \(self.customers["customers"][i] ["sysName"].string!)")
//VERY SLOW
//create a customer object
let customer = Customer( _name: self.customers["customers"][i]["sysName"].string!, _id: self.customers["customers"][i]["ID"].string!, _address: self.customers["customers"][i]["mainAddr"].string!, _contactID: self.customers["customers"][i]["contactID"].string!)
//add customer to customer array
self.customersArray.append(customer)
}
self.layoutViews() //build view, call all table methods
}
Thanks
Improved code:
import Foundation
import UIKit
import Alamofire
import SwiftyJSON
enum SearchMode{
case name
case address
}
class CustomerListViewController: ViewControllerWithMenu, UITableViewDelegate, UITableViewDataSource, UISearchControllerDelegate, UISearchBarDelegate, UISearchDisplayDelegate, UISearchResultsUpdating{
var indicator: SDevIndicator!
var totalCustomers:Int!
//data arrays
var ids = [String]()
var names = [String]()
var addresses = [String]()
var searchController:UISearchController!
var currentSearchMode = SearchMode.name
var customerTableView:TableView = TableView()
var layoutVars:LayoutVars = LayoutVars()
var sections : [(index: Int, length :Int, title: String)] = Array()
var customersSearchResults:[String] = []
var shouldShowSearchResults:Bool = false
let viewsConstraint_V:NSArray = []
let viewsConstraint_V2:NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
title = "Customer List"
view.backgroundColor = layoutVars.backgroundColor
getCustomerList()
}
func getCustomerList() {
//remove any added views (needed for table refresh
for view in self.view.subviews{
view.removeFromSuperview()
}
// Show Indicator
indicator = SDevIndicator.generate(self.view)!
Alamofire.request(API.Router.customerList()).responseJSON() {
response in
//print(response.request ?? "") // original URL request
//print(response.response ?? "") // URL response
//print(response.data ?? "") // server data
//print(response.result) // result of response serialization
do {
if let data = response.data,
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
let customers = json["customers"] as? [[String: Any]] {
for customer in customers {
if let id = customer["ID"] as? String {
self.ids.append(id)
}
if let name = customer["sysName"] as? String {
self.names.append(name)
}
if let address = customer["mainAddr"] as? String {
self.addresses.append(address)
}
}
}
} catch {
print("Error deserializing JSON: \(error)")
}
// build sections based on first letter(json is already sorted alphabetically)
var index = 0;
var firstCharacterArray:[String] = [" "]
for i in 0 ..< self.names.count {
let stringToTest = self.names[i].uppercased()
let firstCharacter = String(stringToTest[stringToTest.startIndex])
if(i == 0){
firstCharacterArray.append(firstCharacter)
}
if !firstCharacterArray.contains(firstCharacter) {
let title = firstCharacterArray[firstCharacterArray.count - 1]
firstCharacterArray.append(firstCharacter)
let newSection = (index: index, length: i - index, title: title)
self.sections.append(newSection)
index = i;
}
if(i == self.names.count - 1){
let title = firstCharacterArray[firstCharacterArray.count - 1]
let newSection = (index: index, length: i - index, title: title)
self.sections.append(newSection)
}
}
self.layoutViews()
}
}
func layoutViews(){
indicator.dismissIndicator()
searchController = UISearchController(searchResultsController: nil)
searchController.searchBar.placeholder = "Search Customers"
searchController.searchResultsUpdater = self
searchController.delegate = self
searchController.searchBar.delegate = self
searchController.dimsBackgroundDuringPresentation = false
searchController.hidesNavigationBarDuringPresentation = false
navigationItem.titleView = searchController.searchBar
let items = ["Name","Address"]
let customSC = SegmentedControl(items: items)
customSC.selectedSegmentIndex = 0
customSC.addTarget(self, action: #selector(self.changeSearchOptions(sender:)), for: .valueChanged)
self.view.addSubview(customSC)
self.customerTableView.delegate = self
self.customerTableView.dataSource = self
self.customerTableView.register(CustomerTableViewCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(self.customerTableView)
//auto layout group
let viewsDictionary = [
"view2":customSC,
"view3":self.customerTableView
]as [String:AnyObject]
let sizeVals = ["fullWidth": layoutVars.fullWidth,"width": layoutVars.fullWidth - 30,"navBottom":layoutVars.navAndStatusBarHeight,"height": self.view.frame.size.height - 100] as [String:Any]
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view2(fullWidth)]", options: [], metrics: sizeVals, views: viewsDictionary))
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view3(fullWidth)]", options: [], metrics: sizeVals, views: viewsDictionary))
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-navBottom-[view2(40)][view3(height)]", options: [], metrics: sizeVals, views: viewsDictionary))
}
/////////////// Search Methods ///////////////////////
func changeSearchOptions(sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
currentSearchMode = .name
break
case 1:
currentSearchMode = .address
break
default:
currentSearchMode = .name
break
}
filterSearchResults()
}
func updateSearchResults(for searchController: UISearchController) {
filterSearchResults()
}
func filterSearchResults(){
customersSearchResults = []
switch currentSearchMode {
case .name:
self.customersSearchResults = self.names.filter({( aCustomer: String ) -> Bool in
return (aCustomer.lowercased().range(of: self.searchController.searchBar.text!.lowercased()) != nil) })
break
case .address:
self.customersSearchResults = self.addresses.filter({( aCustomer: String) -> Bool in
return (aCustomer.lowercased().range(of: self.searchController.searchBar.text!.lowercased()) != nil)
})
break
}
self.customerTableView.reloadData()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
//print("searchBarTextDidBeginEditing")
shouldShowSearchResults = true
self.customerTableView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
shouldShowSearchResults = false
self.customerTableView.reloadData()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
if !shouldShowSearchResults {
shouldShowSearchResults = true
self.customerTableView.reloadData()
}
searchController.searchBar.resignFirstResponder()
}
/////////////// TableView Delegate Methods ///////////////////////
func numberOfSections(in tableView: UITableView) -> Int {
if shouldShowSearchResults{
return 1
}else{
return sections.count
}
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
//print("titleForHeaderInSection")
if shouldShowSearchResults{
return nil
}else{
if(sections[section].title == "#"){
return " # \(self.totalCustomers) Customers Found"
}else{
return " " + sections[section].title //hack way of indenting section text
}
}
}
func sectionIndexTitles(for tableView: UITableView) -> [String]?{
print("sectionIndexTitlesForTableView 1")
if shouldShowSearchResults{
return nil
}else{
//print("sectionIndexTitlesForTableView \(sections.map { $0.title })")
return sections.map { $0.title }
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
//print("heightForHeaderInSection")
if shouldShowSearchResults{
return 0
}else{
return 50
}
}
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
return index
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//print("numberOfRowsInSection")
if shouldShowSearchResults{
return self.customersSearchResults.count
} else {
return sections[section].length
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = customerTableView.dequeueReusableCell(withIdentifier: "cell") as! CustomerTableViewCell
customerTableView.rowHeight = 50.0
if shouldShowSearchResults{
let searchString = self.searchController.searchBar.text!.lowercased()
if(currentSearchMode == .name){
cell.nameLbl.text = self.customersSearchResults[indexPath.row]
cell.name = self.customersSearchResults[indexPath.row]
if let i = self.names.index(of: cell.nameLbl.text!) {
//print("\(cell.nameLbl.text!) is at index \(i)")
cell.addressLbl.text = self.addresses[i]
cell.address = self.addresses[i]
cell.id = self.ids[i]
} else {
cell.addressLbl.text = ""
cell.address = ""
cell.id = ""
}
//text highlighting
let baseString:NSString = cell.name as NSString
let highlightedText = NSMutableAttributedString(string: cell.name)
var error: NSError?
let regex: NSRegularExpression?
do {
regex = try NSRegularExpression(pattern: searchString, options: .caseInsensitive)
} catch let error1 as NSError {
error = error1
regex = nil
}
if let regexError = error {
print("Oh no! \(regexError)")
} else {
for match in (regex?.matches(in: baseString as String, options: NSRegularExpression.MatchingOptions(), range: NSRange(location: 0, length: baseString.length)))! as [NSTextCheckingResult] {
highlightedText.addAttribute(NSBackgroundColorAttributeName, value: UIColor.yellow, range: match.range)
}
}
cell.nameLbl.attributedText = highlightedText
}else{//address search mode
cell.addressLbl.text = self.customersSearchResults[indexPath.row]
cell.address = self.customersSearchResults[indexPath.row]
if let i = self.addresses.index(of: cell.addressLbl.text!) {
cell.nameLbl.text = self.names[i]
cell.name = self.names[i]
cell.id = self.ids[i]
} else {
cell.nameLbl.text = ""
cell.name = ""
cell.id = ""
}
//text highlighting
let baseString:NSString = cell.address as NSString
let highlightedText = NSMutableAttributedString(string: cell.address)
var error: NSError?
let regex: NSRegularExpression?
do {
regex = try NSRegularExpression(pattern: searchString, options: .caseInsensitive)
} catch let error1 as NSError {
error = error1
regex = nil
}
if let regexError = error {
print("Oh no! \(regexError)")
} else {
for match in (regex?.matches(in: baseString as String, options: NSRegularExpression.MatchingOptions(), range: NSRange(location: 0, length: baseString.length)))! as [NSTextCheckingResult] {
highlightedText.addAttribute(NSBackgroundColorAttributeName, value: UIColor.yellow, range: match.range)
}
}
cell.addressLbl.attributedText = highlightedText
}
} else {
//print("make cell")
cell.id = self.ids[sections[indexPath.section].index + indexPath.row]
cell.name = self.names[sections[indexPath.section].index + indexPath.row]
cell.address = self.addresses[sections[indexPath.section].index + indexPath.row]
cell.nameLbl.text = self.names[sections[indexPath.section].index + indexPath.row]
cell.addressLbl.text = self.addresses[sections[indexPath.section].index + indexPath.row]
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexPath = tableView.indexPathForSelectedRow;
let currentCell = tableView.cellForRow(at: indexPath!) as! CustomerTableViewCell
let customerViewController = CustomerViewController(_customerID: currentCell.id)
navigationController?.pushViewController(customerViewController, animated: false )
tableView.deselectRow(at: indexPath!, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

getting PickerData data from a Rest WebService

i get data from a web service and my pickerData Array don't save values when i want to use it outside of the Json Parsing bloc.
here's my code
var pickerData: [String] = [String]()
var mag : String!
override func viewDidLoad() {
super.viewDidLoad()
NomMAG.alpha = 0
// \(detectionString)
let str = "http://vps43623.ovh.net/yamoinscher/api/getAllMag"
let url = NSURL(string: str)!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(urlContent, options: [])
if let jsonResult = jsonObject as? [String:AnyObject] {
if let Pick = jsonResult["magasin"] as? [[String:String]] {
for categorie in Pick {
self.mag = categorie["libelle"]!
self.pickerData.append(magasin)
//self.pickerData = [(self.produits[0].magasin)]
}
print(self.pickerData)
dispatch_async(dispatch_get_main_queue()) {
self.picker1.reloadInputViews()
// print(self.produits.count)
}
}
}
} catch {
print("JSON serialization failed", error)
}
} else if let connectionError = error {
print("connection error", connectionError)
}
}
task.resume()
//print(produits.count)
//pickerData = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "autre"]
//print(self.pickerData)
self.picker1.delegate = self
self.picker1.dataSource = self
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// The number of columns of data
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
// The number of rows of data
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return self.pickerData.count
}
// The data to return for the row and component (column) that's being passed in
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return self.pickerData[row]
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerData[row] == "autre"
{
//print(row)
NomMAG.alpha = 1
}
else
{
//print(row.description)
NomMAG.alpha = 0
}
}
I want to get my PickerView full with the data i gained from the JsonParsing and the PickerData Array is null outside the block of code of the Json
Connect delegate and dataSource of the picker view in Interface Builder and replace viewDidLoad with
var pickerData = [String]()
override func viewDidLoad() {
super.viewDidLoad()
let str = "http://vps43623.ovh.net/yamoinscher/api/getAllMag"
let url = NSURL(string: str)!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(urlContent, options: [])
if let jsonResult = jsonObject as? [String:AnyObject],
magasin = jsonResult["magasin"] as? [[String:String]] {
// filter valid items, map them to an array and filter empty strings
self.pickerData = magasin.filter { $0["libelle"] != nil }.map { $0["libelle"]! }.filter { !$0.isEmpty}
}
dispatch_async(dispatch_get_main_queue()) {
self.picker1.reloadAllComponents()
}
} catch {
print("JSON serialization failed", error)
}
} else if let connectionError = error {
print("connection error", connectionError)
}
}
task.resume()
}
You have to add the NomMAG line

parse containedIn query returns nothing

The following are my codes:
var food = [chicken, fish, duck, pork]
var recipeNames = [String]()
var recipePictures = [UIImage]()
func getRecipes() {
let getRecipesQuery = PFQuery(className: "recipes")
getRecipesQuery.whereKey("food", containedIn: food)
getRecipesQuery.findObjectsInBackgroundWithBlock { (recipes, error) -> Void in
if let recipes = recipes {
self.recipeNames.removeAll(keepCapacity: true)
self.recipePictures.removeAll(keepCapacity: true)
for recipe in recipes {
self.recipeNames.append(recipe["name"] as! String)
if let recipePictureFile = recipe["pictureFile"] as? PFFile {
recipePictureFile.getDataInBackgroundWithBlock({ (imageData, error) -> Void in
if error == nil {
if let picture = UIImage(data: imageData!) {
self.recipePictures.append(picture)
}
}
})
}
}
}
print(self.recipeNames.count)
print(self.recipePictures.count)
}
}
I printed the two arrays counts at the end to check if anything is fetched, the counts are both 0. Any idea why this is happening? There are definitely objects in the class which keys "food" equal to one of the objects in the "food" array, thanks!
EDIT
func getRecipes() {
let getRecipesQuery = PFQuery(className: "recipes")
getRecipesQuery.whereKey("food", containedIn: food)
getRecipesQuery.findObjectsInBackgroundWithBlock { (recipes, error) -> Void in
if let recipes = recipes {
self.recipeNames.removeAll(keepCapacity: true)
self.recipePictures.removeAll(keepCapacity: true)
for recipe in recipes {
self.recipeNames.append(recipe["name"] as! String)
if let recipePictureFile = recipe["pictureFile"] as? PFFile {
recipePictureFile.getDataInBackgroundWithBlock({ (imageData, error) -> Void in
if error == nil {
if let picture = UIImage(data: imageData!) {
self.recipePictures.append(picture)
}
}
})
}
}
dispatch_async(dispatch_get_main_queue()) {
self.recipesTableView.reloadData()
}
}
}
}

Resources