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()
}
}
}
}
Related
I am trying to have two arrays, used as dataSources for two tableViews. One array contains user type: macro and the other array contains user type: micro.
If a user type is changed from macro to micro (and vice-versa) I want to remove the user from one table and add it to the other.
Currently I am able to update the user's tag if changed in Firebase Database and have it appear in the proper array when the app is restarted. The observe function only collects it once and if it is changed, it doesn't update the table until the user quits the app and reopens it. The function I am using to observe.childChanged doesn't seem to update the arrays immediately on the user's app unless they do what was mentioned previously.
My main problem is that my tables are not displaying the users to the table. I am able to access their node and user but they are not appearing in my tables.
Here is the code for my user class:
import UIKit
import Firebase
class User: NSObject {
var Name: String?
var Email: String?
var UID: String?
var Tag: String?
init?(from snapshot: DataSnapshot) {
let dictionary = snapshot.value as? [String: Any]
self.Name = dictionary!["Name"] as? String
self.Email = dictionary!["Email"] as? String
self.UID = dictionary!["UID"] as? String
self.Tag = dictionary!["Tag"] as? String
}
}
Here is my code for loading and populating my allUsersArray the info appends:
func loadAllUsersAndPopulateArray() {
let ref = Database.database().reference().child("Users")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
for userSnap in allUsersSnapshot {
let user = User(from: userSnap)
self.allUsersArray.append(user!)
self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }
self.observeChangeInUserProperty()
}
})
}
Here is my code for observing the change from Firebase:
func observeChangeInUserProperty() {
let ref = Database.database().reference().child("Users")
ref.observe(.childChanged, with: { snapshot in
let key = snapshot.key
let tag = snapshot.childSnapshot(forPath: "Tag").value as! String // ! = never optional
//get the user from the allUsersArray by its key
if let user = self.allUsersArray.first(where: { $0.Tag == key }) {
if user.Tag != tag { //if the tag changed, handle it
user.Tag = tag //update the allUsersArray
if tag == "Macro" { //if the new tag is Macro remove the user from the Micro array
if let userIndex = self.microUsersArray.firstIndex(where: { $0.Tag == key }) {
self.microUsersArray.remove(at: userIndex)
self.macroUsersArray.append(user) //add user to macro array
}
} else { //new type is micro so remove from macro array
if let userIndex = self.macroUsersArray.firstIndex(where: { $0.Tag == key }) {
self.macroUsersArray.remove(at: userIndex)
self.microUsersArray.append(user)
}
}
//reload the tableviews to reflect the changes
self.tableView.reloadData()
self.microTableView.reloadData()
}
}
})
}
Here is my code for the controller:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var allUsersArray = [User]()
var macroUsersArray = [User]()
var microUsersArray = [User]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
searchBar.delegate = self
tableView.register(UserCell.self, forCellReuseIdentifier: networkCell)
microTableView.delegate = self
microTableView.dataSource = self
microTableView.register(microCell.self, forCellReuseIdentifier: microCell)
loadAllUsersAndPopulateArray()
}
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
if (tableView === self.tableView) {
return 1
}
else if (tableView === self.microTableView) {
// Do something else
return 1
}
fatalError("Invalid table")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (tableView === self.tableView) {
let cell = tableView.dequeueReusableCell(withIdentifier: networkCell, for: indexPath) as! UserCell
let user = macroUsersArray[indexPath.row]
cell.textLabel?.text = user.Name
return cell
} else if (tableView === self.microTableView) {
let cell = tableView.dequeueReusableCell(withIdentifier: microCell, for: indexPath) as! microInfluencerCell
let user = microUsersArray[indexPath.row]
cell.textLabel?.text = user.Name
return cell
} else {
fatalError("Invalid table")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (tableView === self.tableView) {
return macroUsersArray.count
}
else if (tableView === self.microTableView) {
return microUsersArray.count
}
else {
fatalError("Invalid table")
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if (tableView === self.tableView) {
return 72
}
else if (tableView === self.microTableView) {
return 72
}
else {
fatalError("Invalid table")
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if (tableView === self.tableView) {
dismiss(animated: true) {
let userName = self.macroUsersArray[indexPath.row]
self.showSecondViewController(user: userName)
print("Dismiss completed")
}
}
else if (tableView === self.microTableView) {
dismiss(animated: true) {
let userName = self.microUsersArray[indexPath.row]
self.showSecondController(user: userName)
print("Dismiss completed")
}
}
else {
fatalError("Invalid table")
}
}
}
func showSecondViewController(user: User) {
let SecondViewController = SecondViewController()
SecondViewController.user = user
let navigationController = UINavigationController(rootViewController: SecondViewController)
self.present(navigationController, animated: true, completion: nil)
}
func loadAllUsersAndPopulateArray() {
let ref = Database.database().reference().child("Users")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
for userSnap in allUsersSnapshot {
let user = User(from: userSnap)
self.allUsersArray.append(user!)
self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }
self.observeChangeInUserProperty()
}
})
}
func observeChangeInUserProperty() {
let ref = Database.database().reference().child("Users")
ref.observe(.childChanged, with: { snapshot in
let key = snapshot.key
let tag = snapshot.childSnapshot(forPath: "Tag").value as! String // ! = never optional
//get the user from the allUsersArray by its key
if let user = self.allUsersArray.first(where: { $0.Tag == key }) {
if user.Tag != tag { //if the tag changed, handle it
user.Tag = tag //update the allUsersArray
if tag == "Macro" { //if the new tag is Macro remove the user from the Micro array
if let userIndex = self.microUsersArray.firstIndex(where: { $0.Tag == key }) {
self.microUsersArray.remove(at: userIndex)
self.macroUsersArray.append(user) //add user to macro array
}
} else { //new type is micro so remove from macro array
if let userIndex = self.macroUsersArray.firstIndex(where: { $0.Tag == key }) {
self.macroUsersArray.remove(at: userIndex)
self.microUsersArray.append(user)
}
}
//reload the tableviews to reflect the changes
self.tableView.reloadData()
self.microTableView.reloadData()
}
}
})
}
}
I am so close to getting this problem solved. Any help would be amazing.
EDIT:
func loadAllUsersAndPopulateArray() {
let ref = Database.database().reference().child("Users").child("Talent")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
for userSnap in allUsersSnapshot {
let user = User(from: userSnap)
self.allUsersArray.append(user!)
self.allUsersNames.append(user!.Name!)
}
self.observeChangeInUserProperty()
})
self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }
self.tableView.reloadData()
self.microTableView.reloadData()
}
With the help from Jay I was able to figure out the solution!
I needed to be setting my arrays info from within the closure. Whenever a person's tag is updated, the app needs to be closed and reopened to show the new change made in firebase.
Thank you #Jay for all the help!!!
here is the new function code:
func loadAllUsersAndPopulateArray() {
let ref = Database.database().reference().child("Users")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
for userSnap in allUsersSnapshot {
let user = User(from: userSnap)
self.allUsersArray.append(user!)
self.allUsersNames.append(user!.Name!)
self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }
self.tableView.reloadData()
self.microTableView.reloadData()
}
self.observeChangeInUserProperty()
})
The issue you're running into is that Firebase is asynchronous - firebase data is only valid within the closure following the Firebase function.
Code after that closure will execute before the code within the closure so you need to plan to handle that data in an asynchronous way.
Additionally, since you're populating an array with data from firebase and the other arrays as based on the first, there's no reason to filter them over and over; populate the main array and then when that's done, populate the other arrays.
Lastly, tableViews are sensitive and loading them over and over may cause flicker. Again, populate your main array and once that's done and the other arrays as populated, reload the tableViews - once.
Here's how that section of code should look
func loadAllUsersAndPopulateArray() {
let ref = Database.database().reference().child("Users")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
for userSnap in allUsersSnapshot {
let user = User(from: userSnap)
self.allUsersArray.append(user!)
self.allUsersNames.append(user!.Name!)
}
self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }
self.tableView.reloadData()
self.microTableView.reloadData()
self.observeChangeInUserProperty()
})
}
I will also suggest getting rid of the allUsersNames array since those names are also stored in the allUsersArray.
I came into something I would assume is an easy enough problem but somehow I can't think of any way to solve it. It's simply converting an NSArray to an Array, and I think I've done a good job here with this code:
func getTrainingSubCategories(completion: #escaping(Result<[TrainingSubCategory], GetError>) -> Void){
let dataTask = URLSession.shared.dataTask(with: resourceURL){data, _, _ in
guard let jsonData = data else {
completion(.failure(.trainingCategoriesNoDataAvailable))
return
}
do{
let parsedData = try JSONSerialization.jsonObject(with: jsonData) as! [String: Any]
for (key, value) in parsedData {
if key == "categories" {
if let categoriesDictionary: [[String: Any]] = value as? [[String: Any]]{
for categoryDictionary in categoriesDictionary{
for (key, value) in categoryDictionary{
if key == "sub_categories" {
print("1st Type => \(type(of: value)) Value => \(value)")
print("2nd Type => \(type(of: value as? Array<TrainingSubCategory>)) Value => \(value as? Array<TrainingSubCategory>)")
// guard let subCategoryValue = value as? Array<TrainingSubCategory> else{
// fatalError("Error in casting value to array of trainingsubcategory")
// }
}
}
}
}
}
}
}catch {
completion(.failure(.trainingSubCategoriesCannotProcessData))
}
}
dataTask.resume()
}
but the console would log otherwise:
1st Value => (
{
"category_id" = 1;
"company_id" = 50;
"created_at" = "2019-11-07 00:58:37";
"deleted_at" = "<null>";
id = 1;
name = Advertisement;
"updated_at" = "2019-11-07 00:58:37";
},
{
"category_id" = 1;
"company_id" = 50;
"created_at" = "<null>";
"deleted_at" = "<null>";
id = 3;
name = Sales;
"updated_at" = "2019-11-10 23:28:30";
}
)
2nd Value => nil
1st Value => (
{
"category_id" = 2;
"company_id" = 50;
"created_at" = "2019-11-07 19:58:07";
"deleted_at" = "<null>";
id = 6;
name = Inventory;
"updated_at" = "2019-11-07 19:58:07";
}
)
2nd Value => nil
1st Value => (
{
"category_id" = 3;
"company_id" = 50;
"created_at" = "2019-11-10 21:24:22";
"deleted_at" = "<null>";
id = 9;
name = "Human Resource";
"updated_at" = "2019-11-10 21:24:22";
}
)
2nd Value => nil
Am I missing something here? I'm quite new to Swift so I don't actually have much of a guess myself.
So I made the return type simply an array of NSDictionary.
func getTrainingSubCategories(completion: #escaping(Result<[NSDictionary], GetError>) -> Void){
let dataTask = URLSession.shared.dataTask(with: resourceURL){data, _, _ in
guard let jsonData = data else {
completion(.failure(.trainingCategoriesNoDataAvailable))
return
}
var trainingSubCategories = [NSDictionary]()
do{
let parsedData = try JSONSerialization.jsonObject(with: jsonData) as! [String: Any]
for (key, value) in parsedData {
if key == "categories" {
if let categoriesDictionary: [[String: Any]] = value as? [[String: Any]]{
for categoryDictionary in categoriesDictionary{
for (key, value) in categoryDictionary{
if key == "sub_categories" {
if let subCategoriesArray: [Any] = value as? [Any] {
for subCategory in subCategoriesArray{
trainingSubCategories.append(subCategory as! NSDictionary)
}
}
}
}
}
}
}
completion(.success(trainingSubCategories))
}
}catch {
completion(.failure(.trainingSubCategoriesCannotProcessData))
}
}
dataTask.resume()
}
and my function call with the return looks like this:
private func downloadSubCategories(withCompanyID: Int) {
let trainingCategoriesRequest = GetRequests(endpoint: "getTrainingCategories", id: String(withCompanyID))
trainingCategoriesRequest.getTrainingSubCategories{result in
switch result{
case .success(let trainingSubCategories):
for counter in 0 ..< trainingSubCategories.count {
for (key, value) in trainingSubCategories[counter]{
print("\(key) => \(value)")
}
}
case .failure(let error):
print(error)
}
}
}
I was able to map them without casting NSDictionary to TrainingSubCategory. Which, I'm sorry for ya'll looking into the post, is not an answer to the initial question but a workaround. :)
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.
}
}
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
I am trying to append data to an Array and save using TMCache, but it's like I'm doing it wrong. Cause, the data isn't being appended. I keep getting empty array
private var teams: Array<Teams> = Array<Teams>()
private var teamResults: [TeamResult]! {
didSet {
if teamResults.count <= 0 {
return
} else {
self.teams = []
for var index = 0; index < teamResults.count; index++ {
//print(index)
let categoryResult = teamResults[index]
if let categoryBackgroundImage = categoryResult["image"] as? PFFile {
categoryBackgroundImage.getDataInBackgroundWithBlock({ (data, error) -> Void in
if let dataGot = data {
let image = UIImage(data: dataGot)
let appendData = Teams(playing: categoryResult["playing"] as! Bool,
name: categoryResult["name"] as! String,
position: categoryResult["position"] as! Int,
image: image!)
//print(appendData.position)
self.teams.append(appendData)
}
print(self.teams.count) <-- I get 0
})
print(self.teams.count) <-- I get 0
}
}
TMCache.sharedCache().setObject(self.teams, forKey: "Teams")
self.mainTableView.reloadData()
for categ in teams {
print(categ.position)
}
}
}
}
getDataInBackgroundWithBlock works asynchronously. The data is returned later in the block.
You have to put the code to reload the table view into the block and check if the loop is finished.
For example (untested)
private var teams: Array<Teams> = Array<Teams>()
private var teamResults: [TeamResult]! {
didSet {
if teamResults.count <= 0 {
return
} else {
self.teams = []
var index : Int
for index = 0; index < teamResults.count; index++ {
//print(index)
let categoryResult = teamResults[index]
if let categoryBackgroundImage = categoryResult["image"] as? PFFile {
categoryBackgroundImage.getDataInBackgroundWithBlock({ (data, error) -> Void in
if let dataGot = data {
let image = UIImage(data: dataGot)
let appendData = Teams(playing: categoryResult["playing"] as! Bool,
name: categoryResult["name"] as! String,
position: categoryResult["position"] as! Int,
image: image!)
//print(appendData.position)
self.teams.append(appendData)
TMCache.sharedCache().setObject(self.category, forKey: "Teams")
if index == teamResults.count {
self.mainTableView.reloadData()
for categ in teams {
print(categ.position)
}
}
}
})
}
}
}
}
}