Add & Cancel buttons in EventKit not working - swift

Im using event kit to create a reminder, but when I press add or cancel the window does not close. if I go into the calendars app I can see the item in there. I've tried adding "editviewDelegate = self" but I always get an error saying "Cannot assign value of type 'ViewController?' to type 'EKEventEditViewDelegate"
import UIKit
import EventKit
import EventKitUI
class ViewController: UIViewController, EKEventViewDelegate {
func eventViewController(_ controller: EKEventViewController, didCompleteWith action: EKEventViewAction) {
controller.dismiss(animated: true, completion: nil)
}
let store = EKEventStore()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addnew))
}
#objc func addnew() {
store.requestAccess(to: .event) { [weak self] success, error in
if success, error == nil {
DispatchQueue.main.async {
guard let store = self?.store else {return}
let othervc = EKEventEditViewController()
othervc.eventStore = store
othervc.event = EKEvent(eventStore: store)
self?.present(othervc, animated: true, completion: nil)
}
}
}
}

Related

Why can't I subclass 'OCKSurveyTaskViewController' from CareKit?

Note: I tried posting on the apple developer forums 2 days ago and didn't receive any responses as well
I've been literally stuck on this portion of my project because I want to mirror the way apple has their care view setup for displaying tasks to users. I for some reason, can't subclass the 'OCKSurveyTaskViewController' as the error I'm getting is:
"Cannot find type 'OCKSurveyTaskViewController' in scope"
I've reinstalled CareKit through SPM 2 or three times and I can't figure out why I can't subclass it even when I see the exact OCKSurveyTaskViewController.swift file and it listed in the code as an open class in the CareKit/iOS/Task/ViewController directory from Xcode.
Could anyone please give me some guidance or perhaps another way to display the ORKTasks I have established for users on a daily basis in another fashion? I prefer the method apple Is using in their wwdc21 CareKit code along but alas I've been stuck on this for too long and this is needed to get to my next step.
Here's my files code:
import UIKit
import CareKit
import CareKitUI
import CareKitStore
import ResearchKit
import os.log
class StudyTaskFeedViewController: OCKDailyTasksPageViewController, OCKSurveyTaskViewController {}
Here's the code to the OCKSurveyTaskViewController.swift file:
#if !os(watchOS) && canImport(ResearchKit)
import CareKitStore
import CareKitUI
import ResearchKit
import UIKit
// MARK: OCKSurveyTaskViewControllerDelegate
public protocol OCKSurveyTaskViewControllerDelegate: AnyObject {
func surveyTask(
viewController: OCKSurveyTaskViewController,
for task: OCKAnyTask,
didFinish result: Result<ORKTaskViewControllerFinishReason, Error>)
func surveyTask(
viewController: OCKSurveyTaskViewController,
shouldAllowDeletingOutcomeForEvent event: OCKAnyEvent) -> Bool
}
public extension OCKSurveyTaskViewControllerDelegate {
func surveyTask(
viewController: OCKSurveyTaskViewController,
for task: OCKAnyTask,
didFinish result: Result<ORKTaskViewControllerFinishReason, Error>) {
// No-op
}
func surveyTask(
viewController: OCKSurveyTaskViewController,
shouldAllowDeletingOutcomeForEvent event: OCKAnyEvent) -> Bool {
return true
}
}
open class OCKSurveyTaskViewController: OCKTaskViewController<OCKTaskController, OCKSurveyTaskViewSynchronizer>, ORKTaskViewControllerDelegate {
private let extractOutcome: (ORKTaskResult) -> [OCKOutcomeValue]?
public let survey: ORKTask
public weak var surveyDelegate: OCKSurveyTaskViewControllerDelegate?
public convenience init(
task: OCKAnyTask,
eventQuery: OCKEventQuery,
storeManager: OCKSynchronizedStoreManager,
survey: ORKTask,
viewSynchronizer: OCKSurveyTaskViewSynchronizer = OCKSurveyTaskViewSynchronizer(),
extractOutcome: #escaping (ORKTaskResult) -> [OCKOutcomeValue]?) {
self.init(
taskID: task.id,
eventQuery: eventQuery,
storeManager: storeManager,
survey: survey,
viewSynchronizer: viewSynchronizer,
extractOutcome: extractOutcome
)
}
public init(
taskID: String,
eventQuery: OCKEventQuery,
storeManager: OCKSynchronizedStoreManager,
survey: ORKTask,
viewSynchronizer: OCKSurveyTaskViewSynchronizer = OCKSurveyTaskViewSynchronizer(),
extractOutcome: #escaping (ORKTaskResult) -> [OCKOutcomeValue]?) {
self.survey = survey
self.extractOutcome = extractOutcome
super.init(
viewSynchronizer: viewSynchronizer,
taskID: taskID,
eventQuery: eventQuery,
storeManager: storeManager
)
}
override open func taskView(
_ taskView: UIView & OCKTaskDisplayable,
didCompleteEvent isComplete: Bool,
at indexPath: IndexPath,
sender: Any?) {
guard isComplete else {
if let event = controller.eventFor(indexPath: indexPath),
let delegate = surveyDelegate,
delegate.surveyTask(
viewController: self,
shouldAllowDeletingOutcomeForEvent: event) == false {
return
}
let cancelAction = UIAlertAction(
title: "Cancel",
style: .cancel,
handler: nil
)
let confirmAction = UIAlertAction(
title: "Delete", style: .destructive) { _ in
super.taskView(
taskView,
didCompleteEvent: isComplete,
at: indexPath,
sender: sender
)
}
let warningAlert = UIAlertController(
title: "Delete",
message: "Are you sure you want to delete your response?",
preferredStyle: .actionSheet
)
warningAlert.addAction(cancelAction)
warningAlert.addAction(confirmAction)
present(warningAlert, animated: true, completion: nil)
return
}
let surveyViewController = ORKTaskViewController(
task: survey,
taskRun: nil
)
let directory = FileManager.default.urls(
for: .documentDirectory,
in: .userDomainMask
).last!.appendingPathComponent("ResearchKit", isDirectory: true)
surveyViewController.outputDirectory = directory
surveyViewController.delegate = self
present(surveyViewController, animated: true, completion: nil)
}
// MARK: ORKTaskViewControllerDelegate
open func taskViewController(
_ taskViewController: ORKTaskViewController,
didFinishWith reason: ORKTaskViewControllerFinishReason,
error: Error?) {
taskViewController.dismiss(animated: true, completion: nil)
guard let task = controller.taskEvents.first?.first?.task else {
assertionFailure("Task controller is missing its task")
return
}
if let error = error {
surveyDelegate?.surveyTask(
viewController: self,
for: task,
didFinish: .failure(error)
)
return
}
guard reason == .completed else {
return
}
let indexPath = IndexPath(item: 0, section: 0)
guard let event = controller.eventFor(indexPath: indexPath) else {
return
}
guard let values = extractOutcome(taskViewController.result) else {
return
}
let outcome = OCKOutcome(
taskUUID: event.task.uuid,
taskOccurrenceIndex: event.scheduleEvent.occurrence,
values: values
)
controller.storeManager.store.addAnyOutcome(
outcome,
callbackQueue: .main) { result in
if case let .failure(error) = result {
self.surveyDelegate?.surveyTask(
viewController: self,
for: task,
didFinish: .failure(error)
)
}
self.surveyDelegate?.surveyTask(
viewController: self,
for: task,
didFinish: .success(reason)
)
}
}
}
#endif
I encountered the same problem when I try to reproduce the demostration code form the "Recover" app in my own app. After I compared my project settings with the "Recover", I noticed that I missed to add the capability of "HealthKit" in my target app's Sign&Capabilities. After I added this capability, it worked.

Not Receiving Remote Command Center events while using Music Player Controller

I am trying to receive notifications from the remote command center for when the play/pause button is tapped so that I can appropriately update the image for my play/pause button. However, I am not receiving the notification of when the playcommand/pausecommand is pressed (ie. when it should print "tapped play" or "tapped pause"). This is my first time using this library, and so I followed Apples docs and it says to implement a music controller using either a applicationMusicPlayer or applicationQueuePlayer to receive these events, but so far I am not able to. I don't know if there is anything else I need to do besides just setting it up as a applicationQueuePlayer.
Here is my code for a very plain music player controller that produces this situation:
let musicPlayer = MPMusicPlayerController.applicationQueuePlayer
override func viewDidLoad() {
super.viewDidLoad()
setupRemoteControl()
musicPlayer.setQueue(with: .songs())
}
func setupRemoteControl() {
UIApplication.shared.beginReceivingRemoteControlEvents()
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.isEnabled = true
commandCenter.playCommand.addTarget { (_) -> MPRemoteCommandHandlerStatus in
print("tapped play")
return .success
}
commandCenter.pauseCommand.isEnabled = true
commandCenter.pauseCommand.addTarget {(_) -> MPRemoteCommandHandlerStatus in
print("tapped pause")
return .success
}
#IBAction func selectSongs(_ sender: UIButton) {
let controller = MPMediaPickerController(mediaTypes: .music)
controller.allowsPickingMultipleItems = true
controller.popoverPresentationController?.sourceView = sender
controller.delegate = self
present(controller, animated: true, completion: nil)
}
func mediaPicker(_ mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
musicPlayer.setQueue(with: mediaItemCollection)
mediaPicker.dismiss(animated: true, completion: nil)
musicPlayer.play()
}

How to come back to previous scene and reload data?

I have two Scenes on Storyboard : One to show a list of items with plus button on the Bar to go to another Scene that has form to add a new item. I'm saving on Local Storage, in the saving function I want to come back to the previous page with the new data.
This is the saving function:
#IBAction func AddTrack(_ sender: Any) {
let item = TrackItem(context: PersistenceService.context)
item.kms = Int32(kmsField!.text!)!
item.liters = Float(litersField!.text!)!
item.date = textFieldPicker!.text!
PersistenceService.saveContext()
navigationController?.popViewController(animated: true)
self.TrackList.append(item)
self.tableView?.reloadData()
}
Knowing that I'm using this function in viewDidLoad() and viewDidAppear()
func GetData(){
let fetchRequest: NSFetchRequest<TrackItem> = NSFetchRequest<TrackItem>(entityName: "TrackItem")
do{
let TrackList = try PersistenceService.context.fetch(fetchRequest)
self.TrackList = TrackList
self.tableView?.reloadData()
}catch{
}
}
You can use NotificationCenter or Delegation (Depends on what you are trying to achieve)
Example NotificationCenter:
VC1, takes a photo:
buttonTapped / function () {
// Upload a Photo()
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.dismiss(animated: true, completion: nil)
}
}
VC2, shows all photos:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(loadList), name: NSNotification.Name(rawValue: "load"), object: nil)
}
#objc func loadList(notification: NSNotification) {
//get data, reload data, etc
}

UIButton does not bring up MFMailViewController

I'm having an error with a bit of code. I am new to this and I am trying to teach myself. I am finding most of my answers online but can't seem to find anything about this particular issue. I want to send an email within the app but anytime I press the email button, the MFMailViewController does not come up. It is like my UIButton isn't working. But I know I have it as an IBAction. Here is my code so far. Any help is much appreciated.
import UIKit
import MessageUI
class RequestService: UIViewController,MFMailComposeViewControllerDelegate {
#IBOutlet weak var CustomerName: UITextField!
#IBOutlet weak var emailButton: UIButton!
#IBAction func sendEmail(_ sender: UIButton) {
if !MFMailComposeViewController.canSendMail() {
print("Mail services are not available")
let ComposeVC = MFMailComposeViewController()
ComposeVC.mailComposeDelegate = self
ComposeVC.setToRecipients(["jwelch#ussunsolar.com"])
ComposeVC.setSubject("New Support Ticket")
ComposeVC.setMessageBody(CustomerName.text!, isHTML: false)
self.present(ComposeVC, animated: true, completion: nil)
}
func mailComposeController(controller: MFMailComposeViewController,didFinishWithResult result:MFMailComposeResult, error: NSError?) {
// Check the result or perform other tasks.
// Dismiss the mail compose view controller.
controller.dismiss(animated: true, completion: nil)
}
}
}
You made an error in the syntax in your sendMail function. The code you posted will only open the view controller if the device can't send mail. Change it to this:
#IBAction func sendEmail(_ sender: UIButton) {
if !MFMailComposeViewController.canSendMail() {
print("Mail services are not available")
return
}
let composeVC = MFMailComposeViewController()
composeVC.mailComposeDelegate = self
composeVC.setToRecipients(["jwelch#ussunsolar.com"])
composeVC.setSubject("New Support Ticket")
composeVC.setMessageBody(CustomerName.text!, isHTML: false)
self.present(composeVC, animated: true, completion: nil)
}
You only attempt to display the mail controller if the device can't send email. That's backwards.
#IBAction func sendEmail(_ sender: UIButton) {
if MFMailComposeViewController.canSendMail() {
print("Mail services are not available")
let ComposeVC = MFMailComposeViewController()
ComposeVC.mailComposeDelegate = self
ComposeVC.setToRecipients(["jwelch#ussunsolar.com"])
ComposeVC.setSubject("New Support Ticket")
ComposeVC.setMessageBody(CustomerName.text!, isHTML: false)
self.present(ComposeVC, animated: true, completion: nil)
}
}
func mailComposeController(controller: MFMailComposeViewController,didFinishWithResult result:MFMailComposeResult, error: NSError?) {
// Check the result or perform other tasks.
// Dismiss the mail compose view controller.
controller.dismiss(animated: true, completion: nil)
}
And you need the delegate method outside of the other method.

swift - dismissing mail view controller from Sprite Kit

I am trying to add a send email button to a Sprite Kit game. I can get the email dialog to show up. But if I hit cancel, the app will crash or do nothing. If I hit send, the email will send, but the dialog stays. I cannot get the mailComposeController function to fire...please help!
Code:
import Foundation
import UIKit
import MessageUI
class MailViewController: UIViewController, MFMailComposeViewControllerDelegate {
let systemVersion = UIDevice.currentDevice().systemVersion
let devicemodel = UIDevice.currentDevice().model
let appVersion = NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as! String
let appBuild = NSBundle.mainBundle().infoDictionary?["CFBundleVersion"] as! String
let myrootview2 = UIApplication.sharedApplication().keyWindow?.rootViewController
let mailComposerVC = MFMailComposeViewController()
override func viewDidLoad() {
super.viewDidLoad()
}
func sendEmailButtonTapped(sender: AnyObject) {
let mailComposeViewController = configuredMailComposeViewController()
if MFMailComposeViewController.canSendMail() {
self.view.window?.rootViewController = mailComposerVC
print("This is the rootview2: \(myrootview2)")
myrootview2!.presentViewController(mailComposeViewController, animated: true, completion: nil)
} else {
self.showSendMailErrorAlert()
}
}
func configuredMailComposeViewController() -> MFMailComposeViewController {
var msgbody: String
mailComposerVC.mailComposeDelegate = self
msgbody = "\n\nDevice: \(devicemodel)\niOS Version: \(systemVersion)\nApp Version: \(appVersion)\nApp Build Number: \(appBuild)\n"
mailComposerVC.setToRecipients(["test1#test.com"])
mailComposerVC.setSubject("test subject")
mailComposerVC.setMessageBody(msgbody, isHTML: false)
//print(mailComposerVC)
return mailComposerVC
}
func showSendMailErrorAlert() {
let sendMailErrorAlert = UIAlertView(title: "Could Not Send Email", message: "Your device could not send e-mail. Please check e-mail configuration and try again.", delegate: self, cancelButtonTitle: "OK")
sendMailErrorAlert.show()
}
// THIS DOESN'T GET CALLED WHEN SENDING OR CANCELLING EMAIL!
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
let test1 = result.rawValue
print(test1)
print(controller)
print(self)
print(myrootview2)
}
The issue is you are making the mailVC as the root view, you have to present it on your view like given below
#IBAction func sendEmailButtonTapped(sender: AnyObject) {
let mailComposeViewController = configuredMailComposeViewController()
if MFMailComposeViewController.canSendMail() {
self.presentViewController(mailComposeViewController, animated: true, completion: nil)
} else {
self.showSendMailErrorAlert()
}
}
 func mailComposeController(controller: MFMailComposeViewController!, didFinishWithResult result: MFMailComposeResult, error: NSError!) {
controller.dismissViewControllerAnimated(true, completion: nil)
}