Search with UISearchBar through WebServer - mysql

What is the correct procedure to search with UISearchBar through a web server? The following code is what I have tried so far. It is working but not in the right way. I am not getting all the results as it has to be.
class PlacesViewController: UITableViewController, UISearchBarDelegate, UISearchControllerDelegate, UISearchDisplayDelegate {
#IBOutlet var searchController: UISearchBar!
var searchActive: Bool = false
var filtered = [PlacesData]()
var startSearching: Bool = false
var DataTable:[PlacesData] = []
var nameToPass: String!
var totalvisits: String!
var totallikes: String!
override func viewDidLoad() {
tableView.delegate = self
tableView.dataSource = self
searchController.delegate = self
definesPresentationContext = true
searchController.showsCancelButton = true
self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0);
donwloadData()
}
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
searchActive = true;
tableView.reloadData()
print("textdidbegin")
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
searchActive = false;
print("textdidend")
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchActive = false;
searchBar.text = ""
searchBar.resignFirstResponder()
self.filtered.removeAll()
self.tableView.reloadData()
print("textdidcancel")
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
searchActive = false;
print("textdidsearch")
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
print("search")
self.filtered.removeAll()
tableView.reloadData()
Alamofire
.request(.POST, "mysite.com/search.php", parameters: ["type":"Stores", "word": searchText])
.responseJSON { response in
if(response.result.description == "SUCCESS"){
let json = JSON(response.result.value!)
let arrayjson = json.arrayValue
let jsondict = arrayjson[0].dictionaryValue
let firstarr = jsondict["words"]?.arrayValue
for item in firstarr!{
let name = item["name"].stringValue
let long = item["long"].stringValue
let lat = item["lat"].stringValue
print(name)
self.filtered.append(PlacesData(name: name, long: long, lat: lat))
}
}
self.tableView.reloadData()
}
}
func donwloadData(){
Alamofire
.request(.GET, "mysite.com/showplaces.php")
.responseJSON { response in
print(response.result)
if(response.result.description == "SUCCESS"){
let json = JSON(response.result.value!)
let arrayjson = json.arrayValue
let jsondict = arrayjson[0].dictionaryValue
let firstarr = jsondict["words"]?.arrayValue
for item in firstarr!{
let name = item["name"].stringValue
let long = item["long"].stringValue
let lat = item["lat"].stringValue
print(name)
self.DataTable.append(PlacesData(name: name, long: long, lat: lat))
}
self.tableView.reloadData()
}
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (searchActive == true) {
return filtered.count + 1
}else{
return DataTable.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! PlacesCel
let places: PlacesData
if (searchActive == true) {
print("search IS active")
if(indexPath.row == filtered.count){
cell.titleLabel.text = "Add Place"
}else{
places = filtered[indexPath.row]
cell.textLabel?.text = places.name
}
} else {
print("search IS NOT active")
places = DataTable[indexPath.row]
cell.textLabel?.text = places.name
}
return cell
}
I could not find a proper tutorial to find the correct way of doing that. Apparently this is not working properly and whatever I tried it is not working. Any solution or answer would be appreciated. Thank you.

After all the problem was that I was not canceling my requests and every time I was typing a character in the searchbar a new request was added and all were added together at the end after every request was finished. The following code gave me the solution.
if((self.request) != nil){
request?.cancel()
}
self.request = Alamofire()

Related

Tableview Pagination not working in swift

I have added tableview bottom pagination with json data
json response in postman:
"result": {
"page_count": 3,
"per_page": 10,
"post_count": 27,
"Payments": [
{
"id": 132,
"payment_response": null,
code: if i run this code then without scrolling the paymentsServiceCall() calling 4 times but table data showing only first 10 records, why? where am i wrong.
i need initially 10 cells to show on tableview then if i scroll then 10 more cells to load and so on. please guide me
class MyPaymentsViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
private var currentPage = 1
private var totalPages = 1
private var paymentsData = PaymentsHistoryModel(dictionary: NSDictionary()) {
didSet {
totalPages = paymentsData?.result?.page_count ?? 0
tableView.reloadData()
}
}
override func viewWillAppear(_ animated: Bool) {
currentPage = 1
paymentsServiceCall()
}
func paymentsServiceCall() {
let param = ["page_no": currentPage]
serviceCall(param: param, method: .post, url: CommonUrl.student_my_payments) { [weak self] (resp) in
if let _ = resp.dict?["result"] as? NSDictionary {
self?.paymentsData = PaymentsHistoryModel(dictionary: resp.dict as NSDictionary? ?? NSDictionary())
}
}
}
}
extension MyPaymentsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let totalPostCount = paymentsData?.result?.payments?.count
if indexPath.row == totalPostCount - 1, currentPage <= totalPages {
print("in refresh")
currentPage += 1
paymentsServiceCall()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
paymentsData?.result?.payments?.count ?? 0
}
}
Try Using This
class PaginationManager {
var isLoading = false
static let shared = PaginationManager()
private init() { } //Singleton
}
extension PaginationManager {
func isPaging(_ scrollView: UIScrollView) -> Bool {
let offset = scrollView.contentOffset
let bounds = scrollView.bounds
let size = scrollView.contentSize
let inset = scrollView.contentInset
let y = offset.y + bounds.size.height - inset.bottom
let h = size.height
let reload_distance: CGFloat = 100.0
if y > h - reload_distance && !isLoading {
return true
}
return false
}
func isPagingForTopScroll(_ scrollView: UIScrollView) -> Bool {
let offset = scrollView.contentOffset
_ = scrollView.bounds
let size = scrollView.contentSize
_ = scrollView.contentInset
let y = offset.y
_ = size.height
let reload_distance: CGFloat = 100.0
if y < reload_distance && !isLoading {
return true
}
return false
}
}
And for using it in Controller
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if PaginationManager.shared.isPaging(scrollView) {
if !(paymentsData?.result?.payments?.isEmpty ?? true), totalPages > paymentsData?.result?.payments?.count ?? 0 {
guard PaginationManager.shared.isLoading else { return }
track("######Loading: \(viewModel.isLoading)")
//Call your service to fetch new data with an updated page number below
}
}
}
After calling the service again check for appending the data in payments or if it is the first page then replace it with new data and inside the service response update the current page count by 1
You can also create isLoading var in your class if multiple service calls are there.

Open pdf in tableview from a json file

I am in a bind.
My regular developer has secured a full time gig, and I am trying to finish a project.
Literally stuck on the last line of code.....
I have a tableview that parses a json file to populate and open a pdf file when a row is selected.
I reworked the tableview in order to add a searchbar and associated code, which works when running the bundle.
I am, however, stuck with connecting the didSelectRow with the appropriate pdf file.
Can someone please lend a hand? Thanks
import UIKit
import WebKit
// Model
struct Section {
var section: String
var pdfs: [PDF]
}
struct PDF {
var code: String
var name: String
var airport: String
}
class PdfVC: UIViewController {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var webView: WKWebView!
#IBOutlet weak var tableViewLeft: NSLayoutConstraint!
#IBOutlet weak var btnMenuLeft: NSLayoutConstraint!
#IBOutlet weak var btnMenu: UIButton!
#IBOutlet weak var searchBar: UISearchBar!
//json
var jsonData: [Section] = []
var filtedJsonData: [Section] = []
//WebView
var fileName: String?
var isVideo: Bool = false
var btnMenuClose:UIBarButtonItem?
override func viewDidLoad() {
super.viewDidLoad()
hideKeyboardWhenTappedAround()
// Setup TableView
tableView.delegate = self
tableView.dataSource = self
setupData()
tableView.reloadData()
// Setup SearchBar
searchBar.delegate = self
// Setup WebView
if let fileName = fileName {
isVideo = fileName.contains(".mp4")
if let range = fileName.range(of: isVideo ? ".mp4" : ".pdf") {
self.fileName!.removeSubrange(range)
}
}
if let file = Bundle.main.url(forResource: fileName ?? "", withExtension: isVideo ? ".mp4" : ".pdf", subdirectory: nil, localization: nil) {
let request = URLRequest(url: file)
webView.load(request)
}
if let image = UIImage(named: "ico_close") {
navigationItem.rightBarButtonItem = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(onWebTap))
//self.navigationItem.leftItemsSupplementBackButton = true
btnMenuClose = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(onWebTap))
self.navigationItem.rightBarButtonItems = [btnMenuClose!];
}
//navigationItem.title = "Airport Maps"
}
private func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
#objc private func dismissKeyboard() {
view.endEditing(true)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(false, animated: animated)
// title color to white
let titleTextAttributed: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.white, .font: UIFont(name: "HelveticaNeue-Light", size: 20) as Any]
self.navigationController?.navigationBar.titleTextAttributes = titleTextAttributed
// back arrow white
self.navigationController?.navigationBar.tintColor = UIColor.white
// blueish navigation bar
self.navigationController?.navigationBar.barTintColor = UIColor(red: 0.00, green: 0.54, blue: 0.92, alpha: 1.00)
self.navigationController?.navigationBar.isTranslucent = false
showMenu(false, animated: false)
}
private func setupData() {
// Parse data from local json file
if let path = Bundle.main.path(forResource: "airports", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
if let jsonResult = jsonResult as? [Dictionary<String, String>] {
for result in jsonResult {
if !jsonData.contains(where: { $0.section == result["Section"] }) {
let section = Section(section: result["Section"] ?? "", pdfs: [])
jsonData.append(section)
}
let pdf = PDF(code: result["Code"] ?? "", name: result["Filename"] ?? "", airport: result["Name"] ?? "")
let index = jsonData.firstIndex(where: { $0.section == result["Section"] })
if let index = index {
jsonData[index].pdfs.append(pdf)
}
}
}
} catch {
// Handle error
print("error parsing json")
}
}
// Sort data before use
jsonData.sort(by: { $0.section < $1.section })
for index in 0..<jsonData.count {
jsonData[index].pdfs.sort(by: { $0.airport < $1.airport } )
}
filtedJsonData = jsonData
}
#objc func onWebTap() {
var left:CGFloat = 0
if tableViewLeft.constant == 0 {
left = -320
}
tableViewLeft.constant = left
showMenu(left != 0, animated: true)
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseInOut, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
func showMenu(_ show:Bool, animated:Bool) {
if let image = UIImage(named: show ? "ico_open" : "ico_close") {
btnMenuClose?.image = image
//navigationItem.rightBarButtonItem?.image = image
//}
//var left:CGFloat = 20
//if show == false {
// left = -64
}
//btnMenuLeft.constant = left
UIView.animate(withDuration: animated ? 0.3 : 0.0, delay: 0, options: .curveEaseInOut, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
// TableView
extension PdfVC: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return filtedJsonData.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if filtedJsonData[section].pdfs.count == 0 {
return nil
}
return filtedJsonData[section].section
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filtedJsonData[section].pdfs.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PdfCell", for: indexPath) as! PdfCell
let pdf = filtedJsonData[indexPath.section].pdfs[indexPath.row]
cell.setupCell(pdf: pdf)
return cell
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
dismissKeyboard()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let pdf = filtedJsonData[indexPath.section].pdfs[indexPath.row]
//THIS IS WHERE I'M STUCK<<<<<<<<<
self.title = pdf.airport
}
}
// SearchBar
extension PdfVC: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
for (index, data) in jsonData.enumerated() {
// Filter pdfs by code and name
let filtedPdf = data.pdfs.filter { $0.code.lowercased().prefix(searchText.count) == searchText.lowercased() || $0.airport.lowercased().prefix(searchText.count) == searchText.lowercased() }
filtedJsonData[index].pdfs = filtedPdf
}
tableView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.text = ""
tableView.reloadData()
}
}
Got it figured out. Thanks for all who spent some time reading my issue. I changed as follows
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let pdf = filtedJsonData[indexPath.section].pdfs[indexPath.row]
self.fileName = pdf.name
self.title = pdf.airport
DispatchQueue.main.async {
self.reloadWebView()
}
}
and added
private func reloadWebView() {
// Setup WebView
if let fileName = fileName {
isVideo = fileName.contains(".mp4")
if let range = fileName.range(of: isVideo ? ".mp4" : ".pdf") {
self.fileName!.removeSubrange(range)
}
}
if let file = Bundle.main.url(forResource: fileName ?? "", withExtension: isVideo ? ".mp4" : ".pdf", subdirectory: nil, localization: nil) {
let request = URLRequest(url: file)
webView.load(request)
}
}

Swift: Accessing dictionary in JSON with Alamofire and private variables in a Model

I'm pulling out my hair with a problem that I think is pretty simple. I have successfully parsed an array of data from a JSON file to populate a tableview and and a collectionview but I am stuck on attempting to get a dictionary to load into my model.
{
latitude: 42.8821,
longitude: -8.541,
timezone: "Europe/Madrid",
offset: 2,
currently: {
time: 1494674291,
summary: "Drizzle",
icon: "rain",
precipIntensity: 0.1803,
precipProbability: 0.35,
precipType: "rain",
temperature: 14.73,
apparentTemperature: 14.73,
dewPoint: 11.63,
humidity: 0.82,
windSpeed: 7.15,
windBearing: 204,
cloudCover: 0.67,
pressure: 1013.37,
ozone: 378.18
},
I am attempting to access the 'currently' dictionary to populate my currentWeather Model.
import UIKit
import Alamofire
class CurrentWeather {
private var _currentTemp: Double!
private var _date: String!
private var _weatherType: String!
private var _highTemp: Double!
private var _lowTemp: Double!
private var _weatherDesc: String!
var currentTemp: Double {
if _currentTemp == nil {
_currentTemp = 0.0
}
return _currentTemp
}
var date: String {
if _date == nil {
_date = ""
}
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .none
let currentDate = dateFormatter.string(from: Date())
self._date = "Today \(currentDate)"
return _date
}
var weatherType: String {
if _weatherType == nil {
_weatherType = ""
}
return _weatherType
}
var highTemp: Double {
if _highTemp == nil {
_highTemp = 0.0
}
return _highTemp
}
var lowTemp: Double {
if _lowTemp == nil {
_lowTemp = 0.0
}
return _lowTemp
}
var weatherDesc: String {
if _weatherDesc == nil {
_weatherDesc = ""
}
return _weatherDesc
}
init(currentDict: Dictionary<String, AnyObject>) {
if let temperature = currentDict["temperature"] as? Double {
self._currentTemp = temperature
}
if let icon = currentDict["icon"] as? String {
self._weatherType = icon
}
if let summary = currentDict["summary"] as? String {
self._weatherDesc = summary
}
}
}
And my VC code for the Alamofire call looks like this -
import UIKit
import Alamofire
class CityWeatherVC: UIViewController, UITableViewDataSource, UITableViewDelegate, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var cityNameLbl: UILabel!
#IBOutlet weak var currentTempLbl: UILabel!
#IBOutlet weak var dateLbl: UILabel!
#IBOutlet weak var currentWeatherImg: UIImageView!
#IBOutlet weak var currentWeatherType: UILabel!
#IBOutlet weak var dayHighTempLbl: UILabel!
#IBOutlet weak var dayLowTempLbl: UILabel!
var currentWeather: CurrentWeather!
var currentWeathers = [CurrentWeather]()
var longRangeForecast: LongRangeForecast!
var longRangeForecasts = [LongRangeForecast]()
var hourlyForecast: HourlyForecast!
var hourlyForecasts = [HourlyForecast]()
private var _segueData: SegueData!
var segueData: SegueData {
get {
return _segueData
} set {
_segueData = newValue
}
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
collectionView.delegate = self
collectionView.dataSource = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
downloadApiData {
self.updateCurrentWeatherUI()
}
}
func downloadApiData(completed: DownloadComplete) {
let currentWeatherUrl = URL(string: "\(darkSkyUrl)\(segueData.latitude),\(segueData.longitude)?units=si")!
Alamofire.request(currentWeatherUrl).responseJSON { response in
let result = response.result
if let dict = result.value as? Dictionary<String, AnyObject> {
if let currently = dict["currently"] as? Dictionary<String, AnyObject> {
let current = CurrentWeather(currentDict: currently)
print(current) // ISSUE HERE!
}
if let hourly = dict["hourly"] as? Dictionary<String, AnyObject> {
if let data = hourly["data"] as? [Dictionary<String, AnyObject>] {
for obj in data {
let forecast = HourlyForecast(hourlyDict: obj)
self.hourlyForecasts.append(forecast)
}
self.collectionView.reloadData()
}
}
if let daily = dict["daily"] as? Dictionary<String, AnyObject> {
if let data = daily["data"] as? [Dictionary<String, AnyObject>] {
for obj in data {
let forecast = LongRangeForecast(longWeatherDict: obj)
self.longRangeForecasts.append(forecast)
}
self.longRangeForecasts.remove(at: 0)
self.tableView.reloadData()
}
}
}
}
completed()
}
// tableView - long range forecast
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "longRangeForecastCell", for: indexPath) as? LongRangeForecastCell {
let forecast = longRangeForecasts[indexPath.row]
cell.configureCell(longRangeForecast: forecast)
return cell
} else {
return LongRangeForecastCell()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return longRangeForecasts.count
}
// collectionView - hourly forecast
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "hourlyForecastCell", for: indexPath) as? HourlyForecastCell {
let forecast = hourlyForecasts[indexPath.row]
cell.configureCell(hourlyForecast: forecast)
return cell
} else {
return HourlyForecastCell()
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return hourlyForecasts.count
}
func updateCurrentWeatherUI() {
cityNameLbl.text = segueData.cityName
dateLbl.text = currentWeather.date
currentTempLbl.text = "\(currentWeather.currentTemp)"
currentWeatherType.text = currentWeather.weatherDesc
currentWeatherImg.image = UIImage(named: "\(currentWeather.weatherType)L")
dayHighTempLbl.text = "\(Int(currentWeather.highTemp))"
dayLowTempLbl.text = "\(Int(currentWeather.lowTemp))"
}
#IBAction func backButtonPressed(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
}
I have a feeling that the problem has to do with how I am referencing it like the arrays of dictionarys that I handled already. Any help would be greatly appreciated.
Update: result.value received. I am looking to parse the "currently" dictionary into my model then use the variables in the model class to populate my current forecast part of my UI. I can't paste in the entire result as it is larger in characters allowed for the post.
Second Update of result.value:
["latitude": 42.660851, "timezone": Europe/Madrid, "daily": {
data = (
{
apparentTemperatureMax = "18.33";
apparentTemperatureMaxTime = 1495022400;
apparentTemperatureMin = "10.87";
apparentTemperatureMinTime = 1495054800;
cloudCover = "0.87";
dewPoint = "11.73";
humidity = "0.88";
icon = rain;
moonPhase = "0.7";
ozone = "325.51";
precipIntensity = "0.1753";
precipIntensityMax = "0.3327";
precipIntensityMaxTime = 1495040400;
precipProbability = "0.54";
precipType = rain;
pressure = "1019.68";
First of all lets declare a type alias for a JSON dictionary outside any class
typealias JSONDictionary = [String:Any]
The returned result (result.value) is an array rather than a dictionary (see the starting [).
In downloadApiData get the JSON data which is the first item in the array, replace
if let dict = result.value as? Dictionary<String, AnyObject> {
with
if let array = result.value as? [JSONDictionary],
let dict = array.first {
Now pass dict to the initializer (not the value for currently)
let current = CurrentWeather(currentDict: dict)
The values for currentTemp, weatherType and weatherDesc are in the dictionary for key currently, the values for highTemp and lowTemp are in the first dictionary in array data in dictionary daily.
Your class CurrentWeather – uncaged from the private backing variables – is
class CurrentWeather {
let currentTemp: Double
let weatherType: String
let highTemp: Double
let lowTemp: Double
let weatherDesc: String
var date: String {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .none
let currentDate = dateFormatter.string(from: Date())
return "Today \(currentDate)"
}
init(currentDict: JSONDictionary) {
if let currently = currentDict["currently"] as? JSONDictionary {
self.currentTemp = currently["temperature"] as? Double ?? 0.0
self.weatherType = currently["icon"] as? String ?? "n/a"
self.weatherDesc = currently["summary"] as? String ?? "n/a"
} else {
self.currentTemp = 0.0
self.weatherType = "n/a"
self.weatherDesc = "n/a"
}
if let daily = currentDict["daily"] as? JSONDictionary,
let data = daily["data"] as? [JSONDictionary],
let firstDailyDict = data.first {
self.highTemp = firstDailyDict["temperatureMax"] as? Double ?? 0.0
self.lowTemp = firstDailyDict["temperatureMin"] as? Double ?? 0.0
} else {
self.highTemp = 0.0
self.lowTemp = 0.0
}
}
}

how to parse complex JSON and show it inside and outside of CollectionView

I have been dealing with JSON for a month now , getting better day by day. Here is a complex jigsaw I have been circling around. JSON return I get is like this :
{
"main_content": [
{
"product_title": "product 3",
"description": "",
"regular_price": "320",
"sale_price": "",
"product_currency": "$",
"size": [
{
"size_data": "L"
},
{
"size_data": "S"
}
],
"color": [
{
"color_data": "RED"
},
{
"color_data": "WHITE"
}
],
"gallery": [
{
"guid": "http://xxx/wp-content/uploads/2016/11/catagory1.jpg"
},
{
"guid": "http://xxx/wp-content/uploads/2016/11/catagory3.jpg"
}
]
}
]
}
Now , here product_title , description , regular_price , sale_price and product_currency will be outside of array as you can see. For size & color I need to fetch as array outside of my CollectionView , I dunno how to iterate through the array outside of a collectionView or tableView, coz in those I got an indexpath to iterate but I dunno what to do outside a collectionView or tableView. Finally My Image slider will be inside CollectionView therefore gallery items needs to be inside that. Here is visual image of my respective page.
Now here is the POST call using Alamofire in my view
import Foundation
import Alamofire
import SwiftyJSON
//Error could happen for these reason
enum ProductDetailsManagerError: Error {
case network(error: Error)
case apiProvidedError(reason: String)
case authCouldNot(reason: String)
case authLost(reason: String)
case objectSerialization(reason: String)
}
// APIManager Class
class ProductDetailsManager {
// THE RIGHT WAY A.K.A. "THE ONE LINE SINGLETON (NOW WITH PROOF!")
static let sharedInstance = ProductDetailsManager()
func printPublicGists(parameter: [String:Any]? , completionHandler: #escaping (Result<[ProductDetailsJSON]>) -> Void) {
let url: String = "http://xxx/wp-api/products/get_product_informations/"
Alamofire.request(url, method: .post, parameters: parameter, encoding: URLEncoding.default, headers: nil)
.responseJSON { (response) in
guard response.result.error == nil else {
print(response.result.error!)
return
}
guard let value = response.result.value else {
print("no string received in response when swapping oauth code for token")
return
}
print(value)
}
}
func fetchPublicGists(parameter: [String:Any]? , completionHandler: #escaping (Result<[ProductDetailsJSON]>) -> Void) {
let url: String = "http://xxx/wp-api/products/get_product_informations/"
Alamofire.request(url, method: .post, parameters: parameter, encoding: URLEncoding.default, headers: nil)
.responseJSON { response in
let result = self.gistArrayFromResponse(response: response)
completionHandler(result)
}
}
// Download Image from URL
func imageFrom(urlString: String, completionHandler: #escaping (UIImage?, Error?) -> Void) {
let _ = Alamofire.request(urlString)
.response { dataResponse in
// use the generic response serializer that returns Data
guard let data = dataResponse.data else {
completionHandler(nil, dataResponse.error)
return
}
let image = UIImage(data: data)
completionHandler(image, nil)
}
}
//gistArrayFromResponse function
private func gistArrayFromResponse(response: DataResponse<Any>) -> Result<[ProductDetailsJSON]> {
// For Network Error
guard response.result.error == nil else {
print(response.result.error!)
return .failure(RueDu8APIManagerError.network(error: response.result.error!))
}
// JSON Serialization Error, make sure we got JSON and it's an array
guard let jsonArray = response.result.value else {
print("did not get array of homeFeed object as JSON from API")
return .failure(RueDu8APIManagerError.objectSerialization(reason: "Did not get JSON dictionary in response"))
}
//turn JSON into gists
//let gistss = jsonArray.flatMap { HomeFeedJSON(items: $0) }
var gists = [ProductDetailsJSON]()
let jsonR = JSON(jsonArray)
let main_content = jsonR["main_content"].array
for item in main_content! {
gists.append(ProductDetailsJSON(items: item))
}
return .success(gists)
}//gistArrayFromResponse() function ends here
}
here is my model class
import Foundation
import SwiftyJSON
class ProductDetailsJSON {
var _product_title: String?
var _description: String?
var _regular_price: String?
var _sale_price: String?
var _product_currency: String?
var _size: String?
var _color: String?
var _image: URL?
init(items: JSON){
self._product_title = items["product_title"].stringValue
self._description = items["description"].stringValue
self._regular_price = items["regular_price"].stringValue
self._sale_price = items["sale_price"].stringValue
self._product_currency = items["product_currency"].stringValue
let sizeData = items["size"].arrayValue
for itemsIMG in sizeData {
self._size = itemsIMG["size_data"].stringValue
}
let colorData = items["color"].arrayValue
for itemsColor in colorData {
self._size = itemsColor["color_data"].stringValue
}
let galleryImg = items["gallery"].arrayValue
for image in galleryImg {
self._image = image["guid"].URL
}
}
var product_title: String {
if _product_title == nil {
_product_title = ""
}
return _product_title!
}
var description: String {
if _description == nil {
_description = ""
}
return _description!
}
var regular_price: String {
if _regular_price == nil {
_regular_price = ""
}
return _regular_price!
}
var sale_price: String {
if _sale_price == nil {
_sale_price = ""
}
return _sale_price!
}
var product_currency: String {
if _product_currency == nil {
_product_currency = ""
}
return _product_currency!
}
var product_color: String {
if _color == nil {
_color = ""
}
return _size!
}
var product_image: URL {
if _image == nil {
let myURL = "http://www.clker.com/cliparts/d/L/P/X/z/i/no-image-icon-hi.png"
let noImage: URL = URL(string: myURL)!
_image = noImage
}
return _image!
}
}
and here is my controller class where I am struggling to show the size , color and gallery items from JSON
import UIKit
import DropDown
import Alamofire
import SwiftyJSON
class ShopItemVC: UIViewController , UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var contentView: UIView!
#IBOutlet weak var selectedProductImg: UIImageView!
#IBOutlet weak var backgroundCardView1: UIView!
#IBOutlet weak var backgroundCardView2: UIView!
#IBOutlet weak var backgroundCardView3: UIView!
#IBOutlet weak var sizeBtn: NiceButton!
#IBOutlet weak var colorBtn: NiceButton!
#IBOutlet weak var productPrice: UILabel!
#IBOutlet weak var productTitle: UILabel!
// var Title = [ProductDetailsJSON]()
var product_id:Int? //got value from SpecificCatagoryVC
var product_detail = [ProductDetailsJSON]()
var reloadData = 0
let sizeDropDown = DropDown()
let colorDropDown = DropDown()
lazy var dropDowns: [DropDown] = {
return [
self.sizeDropDown,
self.colorDropDown
]
}()
let CatagoryPic = ["catagory1","catagory2","catagory3","catagory4","catagory5","catagory6","c atagory7"]
// let CatagoryPicture = [ProductDetailsJSON]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
sizeBtn.layer.borderWidth = 1.2
sizeBtn.layer.borderColor = UIColor.black.cgColor
colorBtn.layer.borderWidth = 1.2
colorBtn.layer.borderColor = UIColor.black.cgColor
backgroundCardView1.backgroundColor = UIColor.white
backgroundCardView1.layer.cornerRadius = 5.0
backgroundCardView1.layer.masksToBounds = false
backgroundCardView1.layer.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
backgroundCardView1.layer.shadowOffset = CGSize(width: 0, height: 0)
backgroundCardView1.layer.shadowOpacity = 0.8
backgroundCardView2.backgroundColor = UIColor.white
backgroundCardView2.layer.cornerRadius = 5.0
backgroundCardView2.layer.masksToBounds = false
backgroundCardView2.layer.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
backgroundCardView2.layer.shadowOffset = CGSize(width: 0, height: 0)
backgroundCardView2.layer.shadowOpacity = 0.8
backgroundCardView3.backgroundColor = UIColor.white
backgroundCardView3.layer.cornerRadius = 5.0
backgroundCardView3.layer.masksToBounds = false
backgroundCardView3.layer.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
backgroundCardView3.layer.shadowOffset = CGSize(width: 0, height: 0)
backgroundCardView3.layer.shadowOpacity = 0.8
setupDropDowns()
}
override func viewDidAppear(_ animated: Bool) {
self.scrollView.contentSize = CGSize(width: self.view.frame.width, height: self.view.frame.height + 40)
loadGists(parameter: ["product_id":product_id ?? 0])
}
func setupDropDowns() {
setupSizeDropDown()
setupColorDropDown()
}
func setupSizeDropDown() {
sizeDropDown.anchorView = sizeBtn
sizeDropDown.bottomOffset = CGPoint(x: 0, y: sizeBtn.bounds.height)
// You can also use localizationKeysDataSource instead. Check the docs.
sizeDropDown.dataSource = [
"XXL",
"XL",
"L",
"M",
"S"
]
// Action triggered on selection
sizeDropDown.selectionAction = { [unowned self] (index, item) in
self.sizeBtn.setTitle(item, for: .normal)
print(item)
}
}
func setupColorDropDown() {
colorDropDown.anchorView = colorBtn
colorDropDown.bottomOffset = CGPoint(x: 0, y: colorBtn.bounds.height)
// You can also use localizationKeysDataSource instead. Check the docs.
colorDropDown.dataSource = [
"Red",
"Blue",
"White",
"Purple",
"Pink"
]
// Action triggered on selection
colorDropDown.selectionAction = { [unowned self] (index, item) in
self.colorBtn.setTitle(item, for: .normal)
print(item)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadGists(parameter: [String:Any]?) {
ProductDetailsManager.sharedInstance.fetchPublicGists(parameter: parameter) {
(result) in
guard result.error == nil else {
self.handleLoadGistsError(result.error!)
return
}
if let fetchedGists = result.value {
self.product_detail = fetchedGists
}
self.reloadData = 1
self.collectionView?.reloadData()
}
}
func handleLoadGistsError(_ error: Error) { }
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return CatagoryPic.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ShopItemCell", for: indexPath) as! ShopItemCell
if reloadData == 1 {
let myProduct = self.product_detail[indexPath.row]
self.productTitle.text = myProduct.product_title
}
cell.shopItemPic.image = UIImage(named: CatagoryPic[(indexPath as NSIndexPath).row])
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// here you know which item is selected by accessing indexPath.item property, for example:
let selectedImage = UIImage(named: CatagoryPic[(indexPath as NSIndexPath).item])
selectedProductImg.image = selectedImage
}
#IBAction func sizeBtnPressed(_ sender: Any) {
sizeDropDown.show()
//print("size btn pressed")
}
#IBAction func colorBtnPressed(_ sender: Any) {
colorDropDown.show()
}
#IBAction func backBtn(_ sender: AnyObject) {
self.dismiss(animated: true, completion: nil)
}
}
There are some other class where I am facing the same issue. Hope If I get this solution I will be able to solve those. Thanks in advance .
First thing to note is that you are returning the size value and not color in the product_color variable.
Also when you loop through the arrays in your JSON, you're setting the variable to only be the final value. For example here:
let sizeData = items["size"].arrayValue
for itemsIMG in sizeData {
self._size = itemsIMG["size_data"].stringValue
}
The JSON is
"size": [
{
"size_data": "L"
},
{
"size_data": "S"
}
]
So _size is going to be set as "S" and "L" will never be assigned. I'd suggest changing _size, _color and _image to
var _sizes: [String] = []
var _colors: [String] = []
var _images: [String] = []
And then when looping through the JSON array:
let sizeData = items["size"].arrayValue
for itemsIMG in sizeData {
let size = itemsIMG["size_data"].stringValue
_sizes.append(size)
}
If I'm understanding correctly, you then want to update your dropdown data when you get the response with the gist.
sizeDropDown.dataSource = product_detail.product_sizes
Then the same for the other drop downs.
And create product_sizes, product_colors and product_images variables in the model similar sale_price and regular_price.

Use of unresolved identifier

In my Swift code to handle push notification I have this image:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tableView:UITableView?
var items = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(animated: Bool) {
let frame:CGRect;(x:0, y: 100, width: self.view.frame.width, height: self.view.frame.height-100)
self.tableView = UITableView(frame: frame)
self.tableView?.dataSource = self
self.tableView?.delegate = self
self.view.addSubview(self.tableView!)
let btn = UIButton(frame: CGRect(x: 0, y: 25, width: self.view.frame.width, height: 50))
btn.backgroundColor = UIColor.cyanColor()
btn.setTitle("EKLE", forState: UIControlState.Normal)
btn.addTarget(self, action: "addData", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(btn)
}
func addData(){
RestApiManager.sharedInstance.getRandomUser { json -> Void in
let results = json["results"]
for (index: String, subJson: JSON) in results{
let user: AnyObject = subJson["user"].object
self.items.addObject(user)
dispatch_async(dispatch_get_main_queue(), {
tableView?.reloadData()
})
}
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell?
if cell == nil{
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "cell")
}
let user:JSON = JSON(self.items[indexPath.row])
let picURL = user["picture"]["medium"].string
let url = NSURL(string: picURL!)
let data = NSData(contentsOfURL: url!)
cell?.textLabel?.text = user["username"].string
cell?.imageView?.image = UIImage(data: data!)
return cell!
}
}
Here is the error:
Use of unresolved identifier 'subJson'
in line let user: AnyObject = subJson["user"].object
How would this be fixed?
If you are using Swift 2 (Xcode 7) the syntax for looping over a dictionary with typed parameters has changed.
Now you should do like this:
for (index, subJson):(String, JSON) in results {
let user: AnyObject = subJson["user"].object
self.items.addObject(user)
dispatch_async(dispatch_get_main_queue(), {
tableView?.reloadData()
})
}
Old way:
(index: String, subJson: JSON)
New way:
(index, subJson):(String, JSON)