Parsing JSON Tableview Information as Separate Strings - ios

I am currently trying to use information in Tableview cells that I have populated with JSON to execute various operations but I am unable to call the specific information due to the fact that it isn't in individual strings. Is there any way to take the group of data I have pulled into each tableview cell and turn it into a series of individual strings? Here is what I currently have:
import UIKit
import GoogleMobileAds
class OngoingViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var userUsernameLabel: UILabel!
#IBOutlet weak var bannerView: GADBannerView!
#IBOutlet weak var ongoingTable: UITableView!
var list:[MyStruct] = [MyStruct]()
struct MyStruct
{
var user1 = ""
var user2 = ""
var wager = ""
var amount = ""
init(_ user1:String, _ user2:String, _ wager:String, _ amount:String)
{
self.user1 = user1
self.user2 = user2
self.wager = wager
self.amount = amount
}
}
override func viewDidLoad() {
super.viewDidLoad()
let username = UserDefaults.standard.string(forKey: "userUsername")!
userUsernameLabel.text = username
/// The banner view.
print("Google Mobile Ads SDK version: " + GADRequest.sdkVersion())
bannerView.adUnitID = "ca-app-pub-3940256099942544/2934735716"
bannerView.rootViewController = self
bannerView.load(GADRequest())
ongoingTable.dataSource = self
ongoingTable.delegate = self
get_data("http://cgi.soic.indiana.edu/~team10/ongoingWagers.php")
}
func get_data(_ link:String)
{
let url:URL = URL(string: link)!
var request = URLRequest(url:url);
request.httpMethod = "POST";
let postString = "a=\(userUsernameLabel.text!)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
self.extract_data(data)
}
task.resume()
}
func extract_data(_ data:Data?)
{
let json:Any?
if(data == nil)
{
return
}
do{
json = try JSONSerialization.jsonObject(with: data!, options: [])
}
catch
{
return
}
guard let data_array = json as? NSArray else
{
return
}
for i in 0 ..< data_array.count
{
if let data_object = data_array[i] as? NSDictionary
{
if let data_user1 = data_object["id"] as? String,
let data_user2 = data_object["id2"] as? String,
let data_wager = data_object["wager"] as? String,
let data_amount = data_object["amount"] as? String
{
list.append(MyStruct(data_user1, data_user2, data_wager, data_amount))
}
}
}
refresh_now()
}
func refresh_now()
{
DispatchQueue.main.async(
execute:
{
self.ongoingTable.reloadData()
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.ongoingTable.dequeueReusableCell(withIdentifier: "owcell", for: indexPath) as! OngoingTableViewCell
cell.infoLabel.text = list[indexPath.row].user1 + " " + list[indexPath.row].user2 + " " + list[indexPath.row].wager + " " + list[indexPath.row].amount
cell.user1Button.tag = indexPath.row
cell.user1Button.addTarget(self, action: #selector(OngoingViewController.user1Action), for: .touchUpInside)
cell.user2Button.tag = indexPath.row
cell.user2Button.addTarget(self, action: #selector(OngoingViewController.user2Action), for: .touchUpInside)
return cell
}
#IBAction func user1Action(sender: UIButton) {
let user1Alert = UIAlertController(title: "Wait a second!", message: "Are you sure this user has won this wager?", preferredStyle: UIAlertControllerStyle.alert)
user1Alert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.default, handler: { action in
let user1ConfirmationAlert = UIAlertController(title: "Great!", message: "Please wait for the other user to confirm the winner of this wager.", preferredStyle: UIAlertControllerStyle.alert)
user1ConfirmationAlert.addAction(UIAlertAction(title: "Got It!", style: UIAlertActionStyle.default, handler: nil))
self.present(user1ConfirmationAlert, animated: true, completion: nil)
}))
user1Alert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default, handler: nil))
self.present(user1Alert, animated: true, completion: nil)
}
#IBAction func user2Action(sender: UIButton) {
let user2Alert = UIAlertController(title: "Wait a second!", message: "Are you sure this user has won this wager?", preferredStyle: UIAlertControllerStyle.alert)
user2Alert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.default, handler: { action in
let user2ConfirmationAlert = UIAlertController(title: "Great!", message: "Please wait for the other user to confirm the winner of this wager.", preferredStyle: UIAlertControllerStyle.alert)
user2ConfirmationAlert.addAction(UIAlertAction(title: "Got It!", style: UIAlertActionStyle.default, handler: nil))
self.present(user2ConfirmationAlert, animated: true, completion: nil)
}))
user2Alert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default, handler: nil))
self.present(user2Alert, animated: true, completion: nil)
}
}
Here is the OngoingTableViewCell subclass:
import UIKit
class OngoingTableViewCell: UITableViewCell {
#IBOutlet weak var infoLabel: UILabel!
#IBOutlet weak var user1Button: UIButton!
#IBOutlet weak var user2Button: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

You have an array of MyStruct structures, that contain entries for user1, user1, wager, and amount. That's good.
You're using tags on the buttons as a way of figuring out the selected cell, which is not ideal. Instead I would suggest using the sender parameter to figure out the indexPath of the cell that contains the button. See the bottom of my answer for the details of a better way to do it.
In any case, once you have a row number, you can easily get to the data for that wager by indexing into your array:
#IBAction func user1Action(sender: UIButton) {
selectedRow = sender.tag
//or, using my function:
selectedRow = tableView.indexPathForView(sender)
//Get the wager for the button the user tapped on.
let thisWager = list[selectedRow]
}
If you want to take action on the wager once the user taps a button in your UIAlertController, don't use a nil handler on the your second alert controller. Instead, pass in a closure that uses the selectedRow parameter from the code above to index into the list of wagers, or even use the thisWager local variable I show in my code.
Getting the indexPath of the button the user taps on:
I created a simple extension to UITableView that lets you pass in a UIView (like the sender from a button action) and get back the indexPath that contains that view.
That extension is dirt-simple. Here's how it looks:
public extension UITableView {
/**
This method returns the indexPath of the cell that contains the specified view
- Parameter view: The view to find.
- Returns: The indexPath of the cell containing the view, or nil if it can't be found
*/
func indexPathForView(_ view: UIView) -> IndexPath? {
let origin = view.bounds.origin
let viewOrigin = self.convert(origin, from: view)
let indexPath = self.indexPathForRow(at: viewOrigin)
return indexPath
}
}
And you can call that function from your button's actions:
#IBAction func buttonTapped(_ button: UIButton) {
if let indexPath = self.tableView.indexPathForView(button) {
print("Button tapped at indexPath \(indexPath)")
}
else {
print("Button indexPath not found")
}
}
The whole project can be found on Github at this link: TableViewExtension

Related

My textView text is not saving itself when i leave the cell

I am trying to do something similar to Notes app and I am stuck with what i think is such a stupid thing. When i enter my app there is a add button to add new notes. I can then rename it and enter to a new view controller which consist of only title and textView on the whole screen. I use Codable and UserDefaults to save the data. Title is saving just fine, but whatever i type in textView and then come back to see all rows in tableView and i come back to that particular "note" all my typed text is gone.
This is my didSelectRowAt method
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier: "Note") as? NoteViewController {
navigationController?.pushViewController(vc, animated: true)
vc.bodyText = note[indexPath.row].body
vc.titleText = note[indexPath.row].title
if note[indexPath.row].title == "New note" {
let ac = UIAlertController(title: "Rename note", message: "Please enter name for a new note", preferredStyle: .alert)
ac.addTextField()
ac.addAction(UIAlertAction(title: "Cancel", style: .default))
ac.addAction(UIAlertAction(title: "Rename", style: .default) { [unowned self, ac] _ in
let newName = ac.textFields![0]
note[indexPath.row].title = newName.text!
self.tableView.reloadData()
vc.viewDidLoad()
self.save()
})
present(ac, animated: true)
} else {
}
}
}
And my NoteViewController
class NoteViewController: UIViewController, UITextViewDelegate {
#IBOutlet var textView: UITextView!
var bodyText: String?
var titleText: String?
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(image: .checkmark, style: .plain, target: self, action: #selector(saveNote))
title = titleText
textView.text = bodyText
}
And Save()
func save() {
let jsonEncoder = JSONEncoder()
if let savedData = try? jsonEncoder.encode(note) {
let defaults = UserDefaults.standard
defaults.set(savedData, forKey: "note")
} else {
print("failed to load data")
}
}
I tried changing my method didSelectRowAt by adding my save() to different places but it's only saving title.
You may use a onSavingTap closure in the NoteViewController to pass the data back to the main view controller.
class NoteViewController: UIViewController, UITextViewDelegate {
var onSavingTap: ((String) -> Void)?
#IBOutlet var textView: UITextView!
var bodyText: String?
var titleText: String?
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(image: .checkmark, style: .plain, target: self, action: #selector(saveNote))
title = titleText
textView.text = bodyText
}
#objc func saveNote() {
onSavingTap?(textView.text ?? "")
}
}
then in the didSelectRow method you set the closure to update your data array as follows:
vc.onSavingTap = { [weak self] body in
self?.note[indexPath.row].body = body
self?.save()
}

How to filter array from selected tableviewcell in swift?

I am quite puzzled on how will I construct my codes regarding on how I will filter the selected array from a tableviewcell. The JSON below is the content of the tableview which displays like
[
{
"hospitalNumber": "00000001",
"patientName": "Test Patient",
"totalAmount": 1111.3
},
{
"hospitalNumber": "00000002",
"patientName": "Test Patient 2",
"totalAmount": 1312
},
{
"hospitalNumber": "00000003",
"patientName": "Test Patient 3",
"totalAmount": 475
}
]
The problem is how can I display the selected hospitalNumber and patientName in the next View Controller, which will display like
This is what my `PaymentDetailsViewController' have:
var patientList: [Patient]! {
didSet {
latestCreditedAmountTableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
getPatientList()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showPatientPaymentDetailsVC" {
if let patientPaymentDetailsVC = segue.destination as? PatientPaymentDetailsViewController {
patientPaymentDetailsVC.isBrowseAll = self.isBrowseAll
if !isBrowseAll {
patientPaymentDetailsVC.patientPayoutDetails = self.selectedPatientPayment
patientPaymentDetailsVC.currentRemittance = self.currentRemittance
patientPaymentDetailsVC.doctorNumber = self.doctorNumber
}
}
}
}
func getPatientList() {
SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.show(withStatus: "Retrieving Patient List")
APIService.PatientList.getPatientList(doctorNumber: doctorNumber, periodId: currentRemittance.periodId) { (patientListArray, error) in
guard let patientListPerPayout = patientListArray, error == nil else {
if let networkError = error {
switch networkError {
case .noRecordFound:
let alertController = UIAlertController(title: "No Record Found", message: "You don't have current payment remittance", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
case .noNetwork:
let alertController = UIAlertController(title: "No Network", message: "\(networkError.rawValue)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
default:
let alertController = UIAlertController(title: "Error", message: "There is something went wrong. Please try again", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
}
}
SVProgressHUD.dismiss()
return
}
self.patientList = patientListPerPayout
self.latestCreditedAmountTableView.reloadData()
SVProgressHUD.dismiss()
return
}
}
**getPerPatientPAyoutDetails(from: String) function**
func getPerPatientPayoutDetails(from: String) {
SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.showInfo(withStatus: "Retrieving Patient Details")
APIService.PatientList.getPatientDetailsPerPayout(periodId: currentRemittance.periodId, doctorNumber: doctorNumber, parameterName: .selectedByHospitalNumber, hospitalNumber: from) { (patientPayout, error) in
guard let patientPerPayoutDetails = patientPayout, error == nil else {
if let networkError = error {
switch networkError {
case .noRecordFound:
let alertController = UIAlertController(title: "No Record Found", message: "You don't have current payment remittance", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
case .noNetwork:
let alertController = UIAlertController(title: "No Network", message: "\(networkError.rawValue)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
default:
let alertController = UIAlertController(title: "Error", message: "There is something went wrong. Please try again", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
}
}
SVProgressHUD.dismiss()
return
}
self.selectedPatientPayment = patientPerPayoutDetails
print(self.selectedPatientPayment)
SVProgressHUD.dismiss()
return
}
}
Base on the gePatientList() function, it will just pull the full list of the patients. I don't know how I will pass the data of the selected patient to another VC. Hope you can help me. Thank you so much.
Codes that might help to understand the flow of my codes
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.section {
case 0: break
case 1: let selectedpatient = patientList[indexPath.row].hospitalNumber
print(selectedpatient!)
self.isBrowseAll = false
getPerPatientPayoutDetails(from: selectedpatient!)
default: break
}
}
Below is the another View Controller that will display the patientName and hospitalNumber
PatientPaymentDetailsVC
class PatientPaymentDetailsViewController: UIViewController {
#IBOutlet weak var patientProcedureTableView: UITableView!
#IBOutlet weak var hospitalNumberLabel: UILabel!
#IBOutlet weak var patientNameLabel: UILabel!
var currentRemittance: CurrentRemittance!
var doctorNumber: String!
var isBrowseAll: Bool!
var patientList: [Patient]!
var patientPayoutDetails: [PatientPayoutDetails]!
override func viewDidLoad() {
super.viewDidLoad()
setupPatientInfo()
}
//MARK: FUNCTION
func setupPatientInfo() {
self.patientNameLabel.text = patient.patientName
self.hospitalNumberLabel.text = patient.hospitalNumber
}
The pulled data under the getPerPatientPayoutDetails function from the didselect will be displayed in PatientPaymentDetailsVC. Below is the output, as you can I see I can pull the data under getPerPatientPayoutDetails but the patientName and hospitalNumber does not display the data.
First of all don't get the data from the table view cell, get it from the data source
Connect the segue to the cell.
Delete the entire method didSelectRowAt
When prepare(for segue is called the sender parameter is the cell.
Get the index path from the cell and get the patient at that index path.
Rather than passing multiple parameters declare a var patient : Patient! property in the destination controller and hand over the patient instance.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "showPatientPaymentDetailsVC",
let cell = sender as? UITableViewCell,
let indexPath = tableView.indexPath(for: cell) else { return }
let patient = patientList[indexPath.row]
getPerPatientPayoutDetails(from: patient.hospitalNumber)
let patientPaymentDetailsVC = segue.destination as! PatientPaymentDetailsViewController
patientPaymentDetailsVC.patient = patient
patientPaymentDetailsVC.patientPayoutDetails = self.selectedPatientPayment
patientPaymentDetailsVC.currentRemittance = self.currentRemittance
patientPaymentDetailsVC.doctorNumber = self.doctorNumber
}
class PatientPaymentDetailsViewController: UIViewController {
#IBOutlet weak var patientProcedureTableView: UITableView!
#IBOutlet weak var hospitalNumberLabel: UILabel!
#IBOutlet weak var patientNameLabel: UILabel!
var currentRemittance: CurrentRemittance!
var doctorNumber = ""
var isBrowseAll = false
var patient : Patient!
var patientPayoutDetails: [PatientPayoutDetails]!
override func viewDidLoad() {
super.viewDidLoad()
setupPatientInfo()
}
//MARK: FUNCTION
func setupPatientInfo() {
self.patientNameLabel.text = patient.patientName
self.hospitalNumberLabel.text = patient.hospitalNumber
}
Side note:
Don't declare patientList as implicit unwrapped optional, declare it as non-optional empty array
var patientList : [Patient]()
Use tableView(_:didSelectRowAt:) method by conforming to UITableViewDelegate.
Get the selected patient as displayed below:
selectedPatient = tableView[indexpath.row] as! [String:Any]
As per your edited question, try this:
let patient = patientList[indexPath.row] as! Patient

Why is my string becoming nil when used as the title of an action sheet?

In an app I am currently writing, I have a string named 'User' which stores the user's name for a game. The value of the string, when printed anywhere else in the Swift file, prints the value that I have set, as an optional.
If I try to use this string as the title of an action sheet action, the string is automatically set to nil, which I can see as both the title of the action and which is printed when I ask it to print(user).
If anyone could shed some light as to why this is happening, or how to prevent it, that would be great. I have also posted my Swift file below, thanks.
import UIKit
class MainViewController: UIViewController {
#IBOutlet weak var segmentedControl: UISegmentedControl!
#IBOutlet weak var firstView: UIView!
#IBOutlet weak var secondView: UIView!
var user:String!
var playerTwo:String!
var playerThree:String!
var playerFour:String!
var playerFive:String!
var playerSix:String!
var userCards = [String]()
override func viewDidLoad() {
super.viewDidLoad()
firstView?.isHidden = false
secondView?.isHidden = true
}
#IBAction func valueDidChange(_ sender: UISegmentedControl) {
switch segmentedControl.selectedSegmentIndex {
case 0:
firstView.isHidden = false
secondView.isHidden = true
case 1:
firstView.isHidden = true
secondView.isHidden = false
default:
break;
}
}
#IBAction func confirm(_ sender: UIButton) {
let alertController = UIAlertController(title: "Action Sheet", message: "What would you like to do?", preferredStyle: .actionSheet)
let userButton = UIAlertAction(title: user /* Here I have tried with putting both 'user', and "\(user)"*/, style: .default, handler: { (action) -> Void in
print("User button tapped")
})
let deleteButton = UIAlertAction(title: "Delete button test", style: .destructive, handler: { (action) -> Void in
print("Delete button tapped")
})
let cancelButton = UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) -> Void in
print("Cancel button tapped")
})
alertController.addAction(userButton)
alertController.addAction(deleteButton)
alertController.addAction(cancelButton)
self.present(alertController, animated: true, completion: nil)
}
}
The value is passed into the above file directly from this code in another file:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showMainController" {
let VC = segue.destination as! MainViewController
VC.user = self.user
if playerTwo != nil {
VC.playerTwo = self.playerTwo
}
if playerThree != nil {
VC.playerThree = self.playerThree
}
if playerFour != nil {
VC.playerFour = self.playerFour
}
if playerFive != nil {
VC.playerFive = self.playerFive
}
if playerSix != nil {
VC.playerSix = self.playerSix
}
}
}
The value is, however, passed through several view controllers, and is initially set here:
if (meTextField.text?.isEmpty)! == false {
let p1 = meTextField.text!
initialPlayersDict["player1"] = "\(p1)"
if errLabelNotBlank {
errorLabel.text = ""
errLabelNotBlank = false
}
}

Self.tableView.reloadData not working if there is no user tap or View change

I am having an issue; I have the following code that get execute in a IBAction and as you will see I have the code to execute reloadData of the tableView. The issue is if I don't touch the screen (like a tap or change of the view) no data is displayed in the tableView but as soon as I touch the screen or move the cells (like just move them up and down) then the data appears.
The following image is the code that imports the contacts, save them in core data and loads them into an Array and execute reload data.
Here is the complete code of the ViewController where this issue is happening.
import UIKit
import CoreData
import AddressBook
class ContactsViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableviewContacts: UITableView!
//Public property that represent an array of the contacts added or copied by the user using the application
var contacts: [Contact] = []
//Public property that represent the context required to save the data in the persistance storage.
var context: NSManagedObjectContext!
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func viewWillAppear(animated: Bool) {
loadContacts()
tableviewContacts.reloadData()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contacts.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:cellContact = tableView.dequeueReusableCellWithIdentifier("cellContact", forIndexPath: indexPath) as! cellContact
let contact = contacts[indexPath.row]
cell.labelContact.text = contact.name + " " + contact.surname
return cell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the specified item to be editable.
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
var contact = contacts[indexPath.row]
var error: NSError?
var handler: HACoreDataHandler = HACoreDataHandler()
handler.context!.deleteObject(contact)
handler.context!.save(&error)
if(error != nil){
let alert = UIAlertController(title: "Error", message: error?.description, preferredStyle: UIAlertControllerStyle.Alert)
var dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction!) ->
Void in
}
alert.addAction(dismiss)
presentViewController(alert, animated: true, completion: nil)
}
contacts.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
#IBAction func importContacts(sender: AnyObject) {
let status = ABAddressBookGetAuthorizationStatus()
if status == .Denied || status == .Restricted {
// user previously denied, to tell them to fix that in settings
let alert = UIAlertController(title: "Warning", message: "We need your permission to import your contacts to giftlog.", preferredStyle: UIAlertControllerStyle.Alert)
var dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction!) ->
Void in
}
alert.addAction(dismiss)
self.presentViewController(alert, animated: true, completion: nil)
return
}
// open it
var error: Unmanaged<CFError>?
let addressBook: ABAddressBook? = ABAddressBookCreateWithOptions(nil, &error)?.takeRetainedValue()
if addressBook == nil {
println(error?.takeRetainedValue())
return
}
ABAddressBookRequestAccessWithCompletion(addressBook) {
granted, error in
if !granted {
// warn the user that because they just denied permission, this functionality won't work
// also let them know that they have to fix this in settings
let alert = UIAlertController(title: "Warning", message: "Please grant access to your contact first by granting the acess to Giftlog in the privacy phone setting ", preferredStyle: UIAlertControllerStyle.Alert)
var dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction!) ->
Void in
}
alert.addAction(dismiss)
self.presentViewController(alert, animated: true, completion: nil)
return
}
if let people = ABAddressBookCopyArrayOfAllPeople(addressBook)?.takeRetainedValue() as? NSArray {
var handler: HACoreDataHandler = HACoreDataHandler()
var error: NSError?
for person:ABRecordRef in people{
if (ABRecordCopyValue(person, kABPersonFirstNameProperty) != nil){
var contact = NSEntityDescription.insertNewObjectForEntityForName("Contact", inManagedObjectContext: handler.context!) as! Contact
contact.name = (ABRecordCopyValue(person, kABPersonFirstNameProperty).takeRetainedValue() as? String)!
contact.surname = (ABRecordCopyValue(person, kABPersonLastNameProperty).takeRetainedValue() as? String)!
var phones : ABMultiValueRef = ABRecordCopyValue(person,kABPersonPhoneProperty).takeUnretainedValue() as ABMultiValueRef
if (ABMultiValueGetCount(phones)>0){
let phoneUnmaganed = ABMultiValueCopyValueAtIndex(phones, 0)
contact.phone = phoneUnmaganed.takeUnretainedValue() as! String
}
let emails: ABMultiValueRef = ABRecordCopyValue(person, kABPersonEmailProperty).takeRetainedValue()
if (ABMultiValueGetCount(emails)>0){
let index = 0 as CFIndex
let emailAddress = ABMultiValueCopyValueAtIndex(emails, index).takeRetainedValue() as! String
contact.email = emailAddress
}
handler.context!.save(&error)
if (error != nil){
let alert = UIAlertController(title: "Error", message: error?.description, preferredStyle: UIAlertControllerStyle.Alert)
var dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction!) ->
Void in
}
alert.addAction(dismiss)
self.presentViewController(alert, animated: true, completion: nil)
} else {
self.contacts.append(contact)
self.tableviewContacts.reloadData()
} // else
} //if (ABRecordCopyValue(person, kABPersonFirstNameProperty) != nil)
} //for person:ABRecordRef in people
} // if let people
} // ABAddressBookRequestAccessWithCompletion
}
func loadContacts(){
var request = NSFetchRequest(entityName: "Contact")
let handler = HACoreDataHandler()
var error: NSError?
contacts = handler.context!.executeFetchRequest(request, error: &error) as! [Contact]
if(error != nil){
let alert = UIAlertController(title: "Error", message: error?.description, preferredStyle: UIAlertControllerStyle.Alert)
var dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction!) ->
Void in
}
alert.addAction(dismiss)
presentViewController(alert, animated: true, completion: nil)
}
}
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the item to be re-orderable.
return true
}
*/
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
}
This image is the code that sets the label of each cell of the tableView.
Finally this is the image from the storyboard file of the view and the prototype cell
Any suggestions will be appreciated.
Thanks in advance.

self.tableview.reloaddata() not being called in swift

class NewsTableViewController: UITableViewController {
#IBOutlet weak var authenticationButton: UIBarButtonItem!
var blogPosts = []
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
self.navigationItem.title = PFConfig.currentConfig().objectForKey("title") as? String
authenticationButton.enabled = true
if let authEnabled:Bool = PFConfig.currentConfig().objectForKey("adminEnabled") as? Bool {
if authEnabled {
authenticationButton.tintColor = UIView.appearance().tintColor
} else {
authenticationButton.tintColor = UIColor.darkGrayColor()
}
}
loadPosts()
//set the title
PFConfig.getConfigInBackgroundWithBlock { (var config: PFConfig!, var error: NSError!) -> Void in
if error == nil {
if let title:String = config.objectForKey("title") as? String {
self.navigationItem.title = title
}
if let authEnabled:Bool = config.objectForKey("adminEnabled") as? Bool {
if authEnabled {
self.authenticationButton.tintColor = UIView.appearance().tintColor
} else {
self.authenticationButton.tintColor = UIColor.darkGrayColor()
}
}
}
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return blogPosts.count
}
#IBAction func authenticate(sender: UIBarButtonItem) {
//check if enabled, and if not, get error message from config
if sender.tintColor != UIColor.darkGrayColor() {
//enabled
var authAlert = UIAlertController(title: "Authenticate", message: "Please login to access the admin page.", preferredStyle: UIAlertControllerStyle.Alert)
authAlert.addTextFieldWithConfigurationHandler({(textField: UITextField!) in
textField.placeholder = "Password"
})
authAlert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil))
authAlert.addAction(UIAlertAction(title: "Go", style: UIAlertActionStyle.Default, handler: { (goAction) -> Void in
let textField:UITextField = authAlert.textFields![0] as UITextField
let text = textField.text
self.authenticateUser(text)
}))
self.presentViewController(authAlert, animated: true, completion: nil)
} else {
//disabled
var serverMessage = PFConfig.currentConfig().objectForKey("adminEnabledMessage") as? String
var errorMessage = UIAlertController(title: "Error", message: "The Admin Console is not enabled right now. Message from server: \(serverMessage!)", preferredStyle: UIAlertControllerStyle.Alert)
errorMessage.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(errorMessage, animated: true, completion: nil)
}
}
//authHandlers
func authenticateUser(password: String) {
//get the server password
let serverPass = PFConfig.currentConfig().objectForKey("password") as? String
if password == serverPass {
//move them to the admin console
self.performSegueWithIdentifier("showConsole", sender: nil)
} else {
//error message
var errorMessage = UIAlertController(title: "Error", message: "Incorrect password, please try again.", preferredStyle: UIAlertControllerStyle.Alert)
errorMessage.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(errorMessage, animated: true, completion: nil)
}
}
func loadPosts() {
let query = PFQuery(className: "Posts")
query.findObjectsInBackgroundWithBlock { (objects, error: NSError!) -> Void in
if error == nil {
self.blogPosts = objects
self.tableView.reloadData()
} else {
var errorMessage = UIAlertController(title: "Error", message: "There was an error retrieving the posts. Please try again later. \(error.localizedDescription)", preferredStyle: UIAlertControllerStyle.Alert)
errorMessage.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(errorMessage, animated: true, completion: nil)
}
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as PostTableViewCell
// Configure the cell...
let currentPost:PFObject = blogPosts.objectAtIndex(indexPath.row) as PFObject
let title = currentPost.objectForKey("title") as String
cell.titleLabelC.text = title
let gmtFormatter:NSDateFormatter = NSDateFormatter()
gmtFormatter.dateFormat = "E MMM d # hh:mm a"
let dateString = gmtFormatter.stringFromDate(currentPost.createdAt)
cell.detailsLabelC.text = dateString
return cell
}
Here is my code for the UITableView that I added to my storyboard. For some reason, when I call self.tableView.reloadData() in loadPosts(), it won't update the table and simply won't call cellForRowAtIndexPath. Any idea why?
Your TableViewDataSource always returns 0 for the number of sections but you need at least 1. Just remove that DataSource function because it is 1 by default.
This error about multithreading.
UITableView - it is UI element. UI works only in main thread. That means that reloadData also works only in main thred.
In your code query.findObjectsInBackgroundWithBlock is async method. So reloadData will not work in this method.
You should call it in main thread through GCD.
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})

Resources