Swift - Parse Data Return in Email - ios

I'm currently building an app where you can read all your last 7 days HeartRate. Once you request the data it get displayed into a UITableView, whenever the data is loaded you can send the results via an email button. Then after the email body get filled up with all the DataHeart. I'm looking to get only the Time & Heartbeat Rate but i can't figured a way to parse it clearly. Also whenever I press the submit or cancel button I can't return to the previous screen. The app doesn't crash or any error, it just wont let me go back.
Here's my code block:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
getHealthKitPermission()
}
let healthStore = HKHealthStore()
var heartRateData: [HKSample]?
#IBOutlet weak var tblHeartRateData: UITableView!
#IBAction func getHeartRate(_ sender: Any) {
getTodaysHeartRate { (result) in
DispatchQueue.main.async {
self.heartRateData = result
self.tblHeartRateData.reloadData()
}
}
}
#IBAction func emailResult(_ sender: Any) {
self.sendEmail()
}
func getTodaysHeartRate(completion: (#escaping ([HKSample]) -> Void)) {
print("func")
let heartRateType:HKQuantityType = HKQuantityType.quantityType(forIdentifier: .heartRate)!
let startDate = Date() - 7 * 24 * 60 * 60
let endDate = Date()
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: [])
let sortDescriptors = [
NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
]
let heartRateQuery = HKSampleQuery(sampleType: heartRateType,
predicate: predicate,
limit: Int(HKObjectQueryNoLimit),
sortDescriptors: sortDescriptors)
{ (query:HKSampleQuery, results:[HKSample]?, error:Error?) -> Void in
guard error == nil else { print("error"); return }
completion(results!)
}
healthStore.execute(heartRateQuery)
}
func sendEmail() {
if let currentHeartRate = heartRateData{
if MFMailComposeViewController.canSendMail() {
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy"
let result = formatter.string(from: date)
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self as? MFMailComposeViewControllerDelegate
mail.setToRecipients([""])
mail.setMessageBody("<p>Your Heart Rate is: \(currentHeartRate)<p>" + result, isHTML: true)
present(mail, animated: true)
} else {
//Do nothing
}
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return heartRateData?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! HeartDataTableViewCell
let heartRateUnit:HKUnit = HKUnit(from: "count/min")
let heartData = self.heartRateData?[indexPath.row]
guard let currData:HKQuantitySample = heartData as? HKQuantitySample else { return UITableViewCell()}
cell.labelTimes.text = "Times: \(startDateSring)"
cell.labelHeartRate.text = "Heart Rate: \(currData.quantity.doubleValue(for: heartRateUnit))"
let startDateFormatter = DateFormatter()
startDateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
startDateFormatter.timeZone = TimeZone.current
let startDateSring = startDateFormatter.string(from: currData.startDate)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return
}
}

Your email sending function should look something like this:
guard let currentHeartRate = heartRateData else {
return
}
guard MFMailComposeViewController.canSendMail() else {
// show alert
return
}
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy"
let mail = MFMailComposeViewController()
// note that your view controller must conform to MFMailComposeViewControllerDelegate
mail.mailComposeDelegate = self
mail.setToRecipients([""])
let heartRateUnit = HKUnit.count().unitDivided(by: .minute())
let quantityStrings = currentHeartRate
.compactMap { $0 as? HKQuantitySample }
.map {
"<p>Your Heart Rate is: \($0.quantity.doubleValue(for: heartRateUnit)) on \(formatter.string(from: $0.startDate)) </p>"
}
mail.setMessageBody(quantityStrings.joined(separator: "<br>"), isHTML: true)
present(mail, animated: true)
Note that you need to conform to the mail compose delegate, e.g.:
extension ViewController: MFMailComposeViewController {
}

Related

Can only delete an object from the Realm it belongs to Swift

I'm trying do delete the note from Realm in my NotesApp and facing this error: "Can only delete an object from the Realm it belongs to". This note has been saved before also in Realm and
could display it in my TableView by tapping on the date in my FSCalendar. I tried to replace realm.add(item) with realm.create(item), but also got the error: "Cannot convert value of type 'T' to expected argument type 'Object.Type' (aka 'RealmSwiftObject.Type')". I'm new in programming, so any help would be appreciated. Here's the relevant code code:
in my ToDoListItem.swift
class ToDoListItem: Object {
#objc dynamic var noteName: String = ""
#objc dynamic var date: Date = Date()
#objc dynamic var descriptionText: String = ""
#objc dynamic var noteImage = Data()
init(date: Date, noteName: String) {
self.date = date
self.noteName = noteName
}
override init() {
self.noteName = ""
self.date = Date()
self.descriptionText = ""
self.noteImage = Data()
}
}
in my RealmManager.swift
class RealmManager {
static let shared = RealmManager()
private let realm = try! Realm()
func write<T: Object>(item: T) {
realm.beginWrite()
realm.add(item)
try! realm.commitWrite()
}
func getObjects<T: Object>(type: T.Type) -> [T] {
return realm.objects(T.self).map({ $0 })
}
func delete<T: Object>(item: T) {
try! realm.write {
realm.delete(item)
}
}
}
in my ViewController where i can edit and delete the notes
#IBAction func didTapDelete() {
let note = ToDoListItem()
RealmManager.shared.delete(item: note)
self.deletionHandler?()
navigationController?.popToRootViewController(animated: true)
}
and finally in my TableViewController where the notes are displayed (honestly i think the problem is hidden here but cannot find it...
#IBOutlet var tableViewPlanner: UITableView!
#IBOutlet var calendarView: FSCalendar!
private var data = [ToDoListItem]()
var datesOfEvents: [String] {
return self.data.map { DateFormatters.stringFromDatestamp(datestamp: Int($0.date.timeIntervalSince1970)) }
}
var items: [ToDoListItem] = []
func getCount(for Date: String) -> Int {
var count: [String : Int] = [:]
for date in datesOfEvents {
count[date] = (count[date] ?? 0) + 1
}
return count[Date] ?? 0
}
func getEventsForDate(date: Date) -> [ToDoListItem] {
let string = DateFormatters.stringFromDatestamp(datestamp: Int(date.timeIntervalSince1970))
return self.data.filter {DateFormatters.stringFromDatestamp(datestamp: Int($0.date.timeIntervalSince1970)) == string }.sorted(by: {$0.date < $1.date})
}
override func viewDidLoad() {
super.viewDidLoad()
calendarView.rounding()
tableViewPlanner.rounding()
data = RealmManager.shared.getObjects(type: ToDoListItem.self)
self.items = self.getEventsForDate(date: Date())
calendarView.delegate = self
calendarView.dataSource = self
tableViewPlanner.delegate = self
tableViewPlanner.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.calendarView.select(Date())
self.calendarView.reloadData()
refresh()
}
//MARK:- TableView Data Source
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count //data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: K.plannerCellIdentifier, for: indexPath) as! NoteTableViewCell
let note = self.items[indexPath.row]
cell.configureCell(note: note)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let note = data[indexPath.row]
guard
let vc = storyboard?.instantiateViewController(identifier: K.infoVCIdentifier) as? InfoViewController else { return }
vc.note = note
vc.deletionHandler = { [weak self] in
self?.refresh()
}
vc.title = note.noteName
navigationController?.pushViewController(vc, animated: true)
}
//MARK:- User Interaction
#IBAction func didTapAddButton() {
guard
let vc = storyboard?.instantiateViewController(identifier: K.entryVCIdentifier) as? EntryViewController else { return }
vc.completionHandler = { [weak self] in
self?.refresh()
}
vc.title = K.entryVCTitle
navigationController?.pushViewController(vc, animated: true)
}
func refresh() {
DispatchQueue.main.async {
self.data = RealmManager.shared.getObjects(type: ToDoListItem.self)
self.tableViewPlanner.reloadData()
self.calendarView.reloadData()
}
}
}
extension PlannerViewController: FSCalendarDelegateAppearance & FSCalendarDataSource {
func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, eventDefaultColorsFor date: Date) -> [UIColor]? {
let dateString = DateFormatters.yearAndMonthAndDateFormatter.string(from: date)
if self.datesOfEvents.contains(dateString) {
return [UIColor.blue]
}
return [UIColor.white]
}
func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
self.items = self.getEventsForDate(date: date)
self.tableViewPlanner.reloadData()
}
func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {
let dateString = DateFormatters.yearAndMonthAndDateFormatter.string(from: date)
let count = self.getCount(for: dateString)
self.tableViewPlanner.reloadData()
return count
}
}
The real problem is the didTapDelete function -- Why are you creating a new note just to delete it (I hope it was only to test out realm delete syntax). You should delete the note object that you passed to the view controller to edit / delete. (vc.note in did select row -> self.note in the other VC - where didTapDelete is)
So your did tap delete will look like --
RealmManager.shared.delete(item: note)
//show deleted alert & go back
A little explanation on the error - Just instantiating a Realm object (ToDoListItem()) does not add it to Realm (the Database system). To delete / edit a realm object, it has to be either fetched from a realm (RealmManager.shared.getObjects(type: ToDoListItem.self)) or added to the realm.
I'd advise going through a Realm tutorial before jumping in the code (there are plenty of them)

duplication using headers section in tableview using Swift

well I'm trying to add date to my header sections every time the user is done with his task. the problem is im kinda new with headers section and for some reason every time I add more than two tasks into the "Done" section, it seems like it duplicates itself.
have looked around stackOverFlow and couldn't find what I needed, and I hope u guys can help me up :)
Here's a pic of the app with the problem: https://imgur.com/wjVy9Uy
Here's my code:
import Foundation
import CoreData
import UIKit
import SwipeCellKit
protocol TaskDelegate {
func updateTaskName(name:String)
}
class TasksManViewController: UITableViewController, SwipeTableViewCellDelegate {
#IBOutlet weak var Sege: UISegmentedControl!
var tasksArray = [Task](){
didSet {
// because we perform this operation on the main thread, it is safe
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
let isSwipeRightEnabled = true
var delegate: TaskDelegate?
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad() {
}
override func viewWillAppear(_ animated: Bool) {
loadTasks()
}
// MARK: - DataSource + Delegate Methods:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasksArray.count
}
override func numberOfSections(in tableView: UITableView) -> Int {
return tasksArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "taskCellRow") as! SwipeTableViewCell
cell.delegate = self
cell.textLabel?.text = tasksArray[indexPath.row].title
return cell
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
if orientation == .left {
guard isSwipeRightEnabled else { return nil }
let doneAction = SwipeAction(style: .destructive, title: "Done") { (action, indexPath) in
//STEP1: Append the task to the doneTasksArr:
self.tasksArray[indexPath.row].isDone = true
//STEP3: Remove the Row:
self.tasksArray.remove(at: indexPath.row)
//STEP4: Update the Model:
self.saveTasks()
self.delegate?.updateTaskName(name: "")
self.tableView.reloadData()
}
let unDoneAction = SwipeAction(style: .destructive, title: "Undone") { (unDoneAction, indexPath) in
self.tasksArray[indexPath.row].isDone = false
self.tasksArray.remove(at: indexPath.row)
self.saveTasks()
}
//configure btn:
doneAction.backgroundColor = .cyan
unDoneAction.backgroundColor = .blue
if Sege.selectedSegmentIndex == 0 { //Doing this to allow the user to unDone tasks.
return [doneAction]
} else { return [unDoneAction] }
} else {
let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in
self.context.delete(self.tasksArray[indexPath.row])
self.tasksArray.remove(at: indexPath.row)
self.saveTasks()
self.delegate?.updateTaskName(name: "")
self.tableView.reloadData()
}
return [deleteAction]
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
// MARK: - Class Methods:
#IBAction func addBtnTapped(_ sender: UIBarButtonItem) {
Sege.selectedSegmentIndex = 0 // Doing this to avoid the user to insert a task into the DoneTasks by mistake! and avoiad a bug :)
insertNewTask()
}
func insertNewTask() {
var textField = UITextField()
let alert = UIAlertController(title: "New Task", message: "Please Add Your Task", preferredStyle: .alert)
alert.addTextField { (alertTextField) in
alertTextField.placeholder = "Create New Task"
textField = alertTextField
}
let action = UIAlertAction(title: "Add", style: .default) { (action) in
let newItem = Task(context: self.context)
newItem.title = textField.text!
newItem.isDone = false
newItem.date = Date()
self.tasksArray.append(newItem)
self.delegate?.updateTaskName(name: newItem.title!)
self.saveTasks()
}
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
// MARK: - Sege Section:
#IBAction func segeControlTapped(_ sender: UISegmentedControl) {
switch Sege.selectedSegmentIndex
{
case 0:
//Loading normal tasks whose are not done
loadTasks()
case 1:
//Loading the doneTasks:
loadDoneTasksFrom()
default:
print("There's something wrong with Sege!")
}
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let completedTasksToDisply = 0
if Sege.selectedSegmentIndex == 1 {
if let firstTask = tasksArray[section].date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
let dateString = dateFormatter.string(from: firstTask)
return " " + dateString + " " + " " + "Completed Tasks: \(completedTasksToDisply)"
}
}
return ""
}
override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
let header: UITableViewHeaderFooterView = view as! UITableViewHeaderFooterView
header.textLabel?.font = UIFont(name: "Verdana", size: 13.0)
header.textLabel?.textAlignment = NSTextAlignment.center
}
//MARK: - Model Manipulation Methods:
func saveTasks() {
do {
try! context.save()
} catch let err {
print("Error Saving context \(err)")
}
}
func loadTasks() {
let request: NSFetchRequest<Task> = Task.fetchRequest()
request.predicate = NSPredicate(format: "isDone == %#", NSNumber(value: false))
request.sortDescriptors = [NSSortDescriptor(key: "isDone", ascending: false)]
do{
tasksArray = try! context.fetch(request)
} catch {
print("There was an error with loading items \(error)")
}
tableView.reloadData()
}
func loadDoneTasksFrom() {
let request:NSFetchRequest<Task> = Task.fetchRequest()
request.predicate = NSPredicate(format: "isDone == %#", NSNumber(value: true))
request.sortDescriptors = [NSSortDescriptor(key: "isDone", ascending: false)]
do{
tasksArray = try context.fetch(request)
} catch {
print("Error fetching data from context\(error)")
}
tableView.reloadData()
}
}
EDIT: Okay I see what you are trying to do. Give this a shot - we will need a double array - each array inside will have to be split by date - our query would look something like this:
var tasksArray = [[Task]]()
var sectionDates = [String]()
func loadTasks() {
tasksArray.removeAll(keepingCapacity: false)
sectionDates.removeAll(keepingCapacity: false)
let request: NSFetchRequest<Task> = Task.fetchRequest()
request.predicate = NSPredicate(format: "isDone == %#", NSNumber(value: false))
request.sortDescriptors = [NSSortDescriptor(key: "isDone", ascending: false)]
do{
let tasks = try! context.fetch(request)
for task in tasks {
if let taskDate = task.date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
let dateString = dateFormatter.string(from: taskDate)
if self.sectionDates.contains(dateString) {
if let section = self.sectionDates.firstIndex(of: dateString) {
self.tasksArray[section].append(task)
}
} else {
self.sectionDates.append(dateString)
// EDIT - ADD THIS LINE (and self. where necessary)
self.tasksArray.append([])
if let section = self.sectionDates.firstIndex(of: dateString) {
self.tasksArray[section].append(task)
}
}
}
}
} catch {
print("There was an error with loading items \(error)")
}
tableView.reloadData()
}
Could also be helpful to load your objects in order but with this set up it should still group everything correctly regardless of the order - unless you want your sections in a specific order then you can specify in query
Now changes to your tableView delegate methods
override func numberOfSections(in tableView: UITableView) -> Int {
return sectionDates.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasksArray[section].count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "taskCellRow") as! SwipeTableViewCell
cell.delegate = self
cell.textLabel?.text = tasksArray[indexPath.section][indexPath.row].title
return cell
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let completedTasksToDisply = 0
if Sege.selectedSegmentIndex == 1 {
let dateString = sectionDates[section]
return " " + dateString + " " + " " + "Completed Tasks: \(completedTasksToDisply)"
}
return ""
}
I figured I'd leave the rest of the tableView delegate methods to you but for the most part we just need to identify the section before the indexPath row when working with the double array - as we did in cellForRow
I ran this code and this was the result:

Modifying Realm data crashes on tableView.reloadData()

You get one thing figured out and something else comes along and that's why I love this stuff.
My workout app uses realm for data persistence and when I modify some of the data elements of the file, it crashes when I reload the tableView. Here is a layout of the tableview in WorkoutViewController.
I select an item to edit it's three properties (Name, Date and average time). When selecting Edit the following view controller is presented. This is WorkoutAlertViewController.
Here is the relevant code:
class WorkoutViewController: UIViewController {
let realm = try! Realm()
var workoutItems: Results<Workout>?
var passedWorkout = Workout()
#IBOutlet weak var tableView: UITableView!
func editWorkout(workout: Workout, name: String, time: String, date: Date) {
do {
try self.realm.write {
passedWorkout = workout
print("passedWorkout \(workout)")
print("changes to be made \(name), \(time), \(date)")
passedWorkout.name = name
passedWorkout.time = time
passedWorkout.dateCreated = date
print("workout changed \(passedWorkout)")
}
} catch {
print("Error editing workout \(error)")
}
// loadWorkout()
}
func loadWorkout() {
workoutItems = realm.objects(Workout.self).sorted(byKeyPath: "name", ascending: false)
print("workoutItems \(String(describing: workoutItems))")
tableView.reloadData()
}
}
extension WorkoutViewController: PassNewWorkout {
func didTapAdd(name: String, time: String, date: Date) {
workoutName = name
averageTime = time
creationDate = date
let newWorkout = Workout()
newWorkout.name = workoutName
newWorkout.dateCreated = creationDate
newWorkout.time = averageTime
saveWorkout(workout: newWorkout)
}
}
extension WorkoutViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return workoutItems?.count ?? 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! WorkoutCell
if let workout = workoutItems?[indexPath.row] {
cell.workoutLabel.text = workout.name
cell.dateLabel.text = String("Created: \(functions.convertTime(timeToConvert: workout.dateCreated!))")
cell.timeLabel.text = String("Avg Time: \(workout.time)")
} else {
cell.textLabel?.text = "No Workouts Added"
}
return cell
}
}
extension WorkoutViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "workoutListSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "workoutListSegue" {
if let destinationVC = segue.destination as? WorkoutListViewController, let indexPath = tableView.indexPathForSelectedRow {
if let workout = workoutItems?[indexPath.row] {
destinationVC.selectedWorkout = workout
destinationVC.workoutName = workout.name
}
}
}
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { (action, view, actionPerformed) in
if let _ = tableView.cellForRow(at: indexPath){
self.passedWorkout = self.workoutItems![indexPath.row]
self.deleteWorkout(workout: self.passedWorkout)
}
tableView.reloadData()
}
deleteAction.backgroundColor = .red
let editAction = UIContextualAction(style: .normal, title: "Edit") { (action, view, actionPerformed) in
if let _ = tableView.cellForRow(at: indexPath){
let alertVC = self.storyboard!.instantiateViewController(identifier: "WorkoutVC") as! WorkoutAlertViewController
self.passedWorkout = self.workoutItems![indexPath.row]
print("editAction \(self.passedWorkout)")
alertVC.didTapEdit(workout: self.passedWorkout)
self.present(alertVC, animated: true, completion: nil)
}
}
editAction.backgroundColor = .gray
return UISwipeActionsConfiguration(actions: [deleteAction, editAction])
}
}
The app crashes on loadWorkout() with:
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file /Users/kevin/Desktop/FitTrax/FitTrax/Controller/Fitness/WorkoutViewController.swift, line 88
Line 88 is tableView.reloadData()
Here is the WorkoutAlertViewController file.
protocol PassNewWorkout {
func didTapAdd(name: String, time: String, date: Date)
}
class WorkoutAlertViewController: UIViewController {
var workoutName = String()
var averageTime = String()
var creationDate = Date()
var receivedWorkout = Workout()
#IBAction func addButtonPressed(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
if workoutName == "" {
workoutName = workoutTextField.text!
creationDate = datePicker.date
averageTime = timeTextField.text!
alertDelegate.didTapAdd(name: workoutName, time: averageTime, date: creationDate)
} else {
workoutName = workoutTextField.text!
creationDate = datePicker.date
averageTime = timeTextField.text!
let workoutVC = storyboard!.instantiateViewController(withIdentifier: "Main") as! WorkoutViewController
workoutVC.editWorkout(workout: receivedWorkout, name: workoutName, time: averageTime, date: creationDate)
}
}
func didTapEdit(workout: Workout) {
workoutName = workout.name
averageTime = workout.time
creationDate = workout.dateCreated!
receivedWorkout = workout
}
}
The edit does work without the reload but does not immediately show the changes. If I go back to the Fitness Menu and back to the Workout Menu, it displays the changes then. It's something with that reload that I can't figure out.
Thank you for any suggestions.

How to save Tomorrow date tasks in core data and how to get these tasks into table view in Swift?

How to save Tomorrow date tasks in core data and how to get these tasks into table view in Swift ?
When i print("Objects are in Tomorrow Task : (filterTomorrowTask.count)"), then it shows nil array.
I dont know how to fix this bug. I am new to Swift, so dont know how to fix this issue.
Utility Class :
class FilteredData
{
static func filterTomorrowTask(filteredObject: String) -> [Task]
{
let fetchRequest: NSFetchRequest<Task> = Task.fetchRequest()
var user: [Task]? = nil
let predicate = NSPredicate(format: "date_from == %#", filteredObject)
fetchRequest.predicate = predicate
do {
user = try context.fetch(fetchRequest)
return user!
}
catch {
return user!
}
}
}
ViewController.h
//CoreData Object
var filterTomorrowTask: [Task] = []
override func viewDidLoad() {
super.viewDidLoad()
self.tomorrowFilterTask()
self.tblToday.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if tableView == self.tblTomorrow
{
print("Tomorrow Count \(filterTomorrowTask.count)")
return filterTomorrowTask.count
}
else
{
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if tableView == self.tblTomorrow
{
let cell = tableView.dequeueReusableCell(withIdentifier: "TomorrowTaskCell", for: indexPath) as! TomorrowTaskTVCell
let filteredResult = filterTomorrowTask[indexPath.row]
cell.lblTask.text = filteredResult.task_name
cell.tfTime.text = filteredResult.time_from
return cell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "TomorrowTaskCell", for: indexPath) as! TomorrowTaskTVCell
return cell
}
}
func tomorrowFilterTask()
{
isTomorrow = true
//Adding 1 to Current Date
let today = Date()
let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: today)
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.dateFormat = "yyyy-MM-dd"
let strTomorrow = formatter.string(from: tomorrow!)
filterTomorrowTask = FilteredData.filterTomorrowTask(filteredObject: strTomorrow)
}
You can Try using Func as Below Once
class FilteredData
{
static func filterTomorrowTask(filteredObject: String) -> [Task]
{
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Your Entity Name")
var user: [Task]? = nil
let predicate = NSPredicate(format: "date_from == %#", filteredObject)
fetchRequest.predicate = predicate
do {
user = try context.fetch(fetchRequest)
return user!
}
catch {
return user!
}
}
}
or just simply Without Creating a Special Instance of User
class FilteredData
{
static func filterTomorrowTask(filteredObject: String) -> [Task]
{
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Your Entity Name")
let predicate = NSPredicate(format: "date_from == %#", filteredObject)
fetchRequest.predicate = predicate
///Handler
do
{
let response = try context.fetch(fetchRequest)
return response as! [Audio]
}
catch let error as NSError
{
///failure
print(error)
return [Audio]()
}
}
}

How to insert the first new row in TableViewCell(swift)?

People tell me how to get out of my position and do so that when a new event is added it becomes the beginning, not the end.I will be very grateful for the help.
I have three arrays and I had to sort them out by the parent cells, and in this line I very much played func tableView (_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
import UIKit
import Parse
class PrognozSegmentViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
var soccerString = [Soccer]()
var basketString = [Basketball]()
var tennisString = [Tennis]()
var refresh : UIRefreshControl!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var segmentControl: UISegmentedControl!
#IBAction func btnSegment(_ sender: Any) {
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
refresh = UIRefreshControl()
refresh.attributedTitle = NSAttributedString(string: "Потяните вниз чтоб обновить ресурс")
refresh.tintColor = UIColor.green
refresh.addTarget(self, action: #selector(PrognozSegmentViewController.refreshing), for: UIControlEvents.valueChanged)
tableView.addSubview(refresh)
setupSeg()
tableView.tableFooterView = UIView()
tableView.estimatedRowHeight = 88
tableView.rowHeight = UITableViewAutomaticDimension
tableView.separatorStyle = UITableViewCellSeparatorStyle.none
tableView.register(UINib(nibName:"TableViewCell",bundle:nil), forCellReuseIdentifier: "sportCell")
loadObjects1()
loadObjects2()
loadObjects3()
}
func setupSeg() {
let attributes = [ NSAttributedStringKey.foregroundColor : UIColor.yellow,
NSAttributedStringKey.font : UIFont.systemFont(ofSize: 10, weight: UIFont.Weight.bold)];
let attributesSelected = [ NSAttributedStringKey.foregroundColor : UIColor.black,
NSAttributedStringKey.font : UIFont.systemFont(ofSize: 12, weight: UIFont.Weight.bold)];
segmentControl.setTitleTextAttributes(attributes, for: UIControlState.normal)
segmentControl.setTitleTextAttributes(attributesSelected, for: UIControlState.selected)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var returnSport = 0
switch (segmentControl.selectedSegmentIndex) {
case 0 :
returnSport = soccerString.count
break
case 1 :
returnSport = basketString.count
break
case 2 :
returnSport = tennisString.count
break
default :
break
}
return returnSport
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let sportCell = tableView.dequeueReusableCell(withIdentifier: "sportCell", for: indexPath) as! TableViewCell
switch (segmentControl.selectedSegmentIndex) {
case 0 :
let soccer = soccerString[indexPath.row]
sportCell.matchLabel.text = soccer.matchS
soccerString[indexPath.row].imagePrS.getDataInBackground {(data, error) in
sportCell.imageMatch.image = error == nil ? UIImage(data: data!) : nil
}
break
case 1 :
let basket = basketString[indexPath.row]
sportCell.matchLabel.text = basket.matchB
basketString[indexPath.row].imagePrB.getDataInBackground {(data, error) in
sportCell.imageMatch.image = error == nil ? UIImage(data: data!) : nil
}
break
case 2:
sportCell.matchLabel.text = tennisString[indexPath.row].matchT
tennisString[indexPath.row].imagePrT.getDataInBackground {(data, error) in
sportCell.imageMatch.image = error == nil ? UIImage(data: data!) : nil
}
break
default:
break
}
sportCell.selectionStyle = UITableViewCellSelectionStyle.none
let date = Date()
let calendar = Calendar.current
let yesterday = calendar.date(byAdding: .day, value: -1, to: date)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE, MMM:d, yyyy"
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .short
dateFormatter.locale = Locale(identifier: "ru_UA")
dateFormatter.doesRelativeDateFormatting = true
let dataFormater2 = DateFormatter()
dataFormater2.dateFormat = "EEEE, MMM:d, yyyy"
dataFormater2.dateStyle = .short
dataFormater2.timeStyle = .short
dataFormater2.locale = Locale(identifier: "ru_UA")
dataFormater2.doesRelativeDateFormatting = true
sportCell.dataSave.text! = dateFormatter.string(from: date)
sportCell.dataSave.text = dateFormatter.string(from: yesterday!)
return sportCell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func loadObjects1() {
let query = Soccer.query() as! PFQuery<Soccer>
query.findObjectsInBackground { (objects, error) in
if error == nil {
self.soccerString.removeAll()
self.soccerString = objects!
self.tableView.reloadData()
} else {
print(error!)
}
}
}
func loadObjects2() {
let query = Basketball.query() as! PFQuery<Basketball>
query.findObjectsInBackground { (objects, error) in
if error == nil {
self.basketString.removeAll()
self.basketString = objects!
self.tableView.reloadData()
} else {
print(error!)
}
}
}
func loadObjects3() {
let query = Tennis.query() as! PFQuery<Tennis>
query.findObjectsInBackground { (objects, error) in
if error == nil {
self.tennisString.removeAll()
self.tennisString = objects!
self.tableView.reloadData()
} else {
print(error!)
}
}
}
#objc func refreshing() {
loadObjects1()
loadObjects2()
loadObjects3()
tableView.reloadData()
refresh.endRefreshing()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch (segmentControl.selectedSegmentIndex) {
case 0 :
if segue.identifier == "showSoccer" {
if let indexPath = tableView.indexPathForSelectedRow {
let destationViewController = segue.destination as! DetailSoccerTableViewController
destationViewController.detailSoccer = self.soccerString[indexPath.row]
}
}
case 1 :
if segue.identifier == "showBasket" {
let dvc = segue.destination as! DetailBasketViewController
if let indexPath = tableView.indexPathForSelectedRow {
let row = Int(indexPath.row)
dvc.baskets = basketString[row]
}
}
case 2 :
let destationVC: DetailTennisViewController = segue.destination as! DetailTennisViewController
if let indexPath = self.tableView.indexPathForSelectedRow {
let row = Int(indexPath.row)
destationVC.tenises = tennisString[row]
}
self.present(destationVC, animated: true, completion: nil)
default :
break
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch ( segmentControl.selectedSegmentIndex) {
case 0 :
let soccer = soccerString[indexPath.row]
performSegue(withIdentifier: "showSoccer", sender: soccer)
break
case 1 :
let basket = basketString[indexPath.row]
let destinationVC = DetailBasketViewController()
destinationVC.baskets = basket
self.performSegue(withIdentifier: "showBasket", sender: self)
break
case 2 :
let tennis = tennisString[indexPath.row]
let destTenVC = DetailTennisViewController()
destTenVC.tenises = tennis
self.performSegue(withIdentifier: "showTennis", sender: self)
break
default :
break
}
}
}
If your problem is the new object becomes the last
I assume you're talking about an array and you're doing
array.append(item)
If you want to have the item at the beginning do:
array.insert(item, at: 0)
And then
tableView.reloadData()

Resources