Swift 3 Create Reminder EKEventStore - swift3

I would like to save reminders to the default reminders location. But when I press my button I get a fatal error: unexpectedly found nil while unwrapping an Optional value... I am pretty new to this and most examples I locate are overly complicated or not in Swift 3.
class ViewController: UIViewController {
var eventStore: EKEventStore?
#IBOutlet weak var reminderText: UITextField!
#IBAction func setReminder(_ sender: Any) {
let reminder = EKReminder(eventStore: self.eventStore!)
reminder.title = "Go to the store and buy milk"
reminder.calendar = (eventStore?.defaultCalendarForNewReminders())!
do {
try eventStore?.save(reminder,
commit: true)
} catch let error {
print("Reminder failed with error \(error.localizedDescription)")
}
}
}

As its a simple piece of code I thought I would post my answer after I figured it out for any future swifters. I always like simple examples.
import UIKit
import EventKit
class ViewController: UIViewController {
var eventStore = EKEventStore()
var calendars:Array<EKCalendar> = []
// Not used at this time
#IBOutlet weak var reminderText: UITextField!
#IBAction func setReminder(_ sender: Any) {
let reminder = EKReminder(eventStore: self.eventStore)
reminder.title = "Go to the store and buy milk"
reminder.calendar = eventStore.defaultCalendarForNewReminders()
do {
try eventStore.save(reminder,
commit: true)
} catch let error {
print("Reminder failed with error \(error.localizedDescription)")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// get permission
eventStore.requestAccess(to: EKEntityType.reminder, completion:
{(granted, error) in
if !granted {
print("Access to store not granted")
}
})
// you need calender's permission for the reminders as they live there
calendars = eventStore.calendars(for: EKEntityType.reminder)
for calendar in calendars as [EKCalendar] {
print("Calendar = \(calendar.title)")
}
}
override func viewWillAppear(_ animated: Bool) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

With the #adamprocter sample, we also need to add "NSRemindersUsageDescription" key with your message in info.plist file. I tried adding this as a comment but I am not eligible.

Related

PHPicker result(s) incomplete

SwiftUI novice here.
My PHPicker results show a weird behaviour.
Whether I pick one image or several, often the result is empty for a single image or incomplete if multiple images are picked.
Oddities: every image that is missing from a PHPicker session result can be fetched in another session (so the image itself is okay), furthermore it happens that in the next session some images are additionally returned that had been selected in the session before but were missing.
There are no explicit error messages in the console, also the behaviour is completely unpredictable.
So let's say I pick 20 images in a session: 9 of them get returned and appended and maybe another 6 of them get returned additionally in the next session without being picked, so there are still 5 images missing which in turn are able to be picked in future sessions.
Further use of the PHPicker results works without problems; dates and paths are sent into Core Data and the images themselves saved to FileManager; this data is then combined in a list view.
I guess it might have to do with the interplay of the 3 parts (date, path, image) I fetch for each image, but I'm at a loss where exactly the problem arises.
struct PhotoPicker: UIViewControllerRepresentable {
#Binding var dates: [Date?]
#Binding var paths: [String?]
#Binding var images: [UIImage?]
#Environment(\.presentationMode) var presentationMode
func makeUIViewController(context: Context) -> PHPickerViewController {
var config = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
config.filter = .images
config.selectionLimit = 20
config.preferredAssetRepresentationMode = .current
let controller = PHPickerViewController(configuration: config)
controller.delegate = context.coordinator
return controller
}
func makeCoordinator() -> PhotoPicker.Coordinator {
return Coordinator(self)
}
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
}
class Coordinator: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
parent.presentationMode.wrappedValue.dismiss()
guard !results.isEmpty else {
return
}
print(results)
for result in results {
if result.itemProvider.canLoadObject(ofClass: UIImage.self) {
if let assetId = result.assetIdentifier {
let assetResults = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: nil)
result.itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.image.identifier) {
(url, error) in
if error != nil {
print("error \(error!)");
} else {
result.itemProvider.loadObject(ofClass: UIImage.self) {
(image, error) in
if error != nil {
print("error \(error!)");
} else {
if assetResults.firstObject?.creationDate != nil && url?.lastPathComponent != nil && image != nil {
Task { #MainActor in
self.parent.dates.append(assetResults.firstObject?.creationDate)
print(assetResults.firstObject?.creationDate as Any)
self.parent.paths.append(url?.lastPathComponent)
print(url?.lastPathComponent as Any)
self.parent.images.append(image as? UIImage)
print(image as Any)
}
}
}
}
}
}
}
}
}
}
private let parent: PhotoPicker
init(_ parent: PhotoPicker) {
self.parent = parent
}
}
}

Youtubeplayer swift 3 error

Hi I am getting this error message in my xcode project.
//Thread 1: EXC_BAD ACCESS(code = 2,address =DX10f8d3b28)
import UIKit
class PlayerViewController: UIViewController {
#IBOutlet weak var youtubeplayerview:YouTubePlayerView!
var videoURL: URL! = URL(string:"https://www.youtube.com/watch?v=Inn2K-V3NFI")
override func viewDidLoad() {
super.viewDidLoad()
if videoURL != nil {
youtubeplayerview.loadVideoURL(videoURL)
//Thread 1: EXC_BAD ACCESS(code = 2,address =DX10f8d3b28)
} else
{
...
}
// Do any additional setup after loading the view.
}
}
That seems that your YouTubePlayerView - outlet is not connected.
So make it optional to validate
#IBOutlet weak var youtubeplayerview:YouTubePlayerView?
And then
if let myPlayer = youtubeplayerview {
myPlayer.loadVideoURL(videoURL)
} else {
print("na na na ... ")
}
You can also remove the nil check for the videoURL, you declared it as implicitly unwrapped optionals https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html
It worked my iboulet was not connected in main storyboard thanks.

iOS - How to insert a new To-Do item in a single view controller?

I want to add a new To Do item when i press the add button,but i don't want to switch to another page.
press the add button to add a new row in the table view,input something and press the done button,it will save.
somebody suggests me to save the cells data to Model,but i don't know how to write this.
Who can help me?
import UIKit
import CoreData
class ToDoViewController: UIViewController {
var items: [NSManagedObject] = []
#IBOutlet weak var tableView: UITableView!
#IBAction func addItem(_ sender: UIBarButtonItem) {
//***How to write this code***
}
#IBAction func done(_ sender: UIBarButtonItem) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "ToDo", in: managedContext)!
let item = NSManagedObject(entity: entity, insertInto: managedContext)
//***let list = the current textView's text
//how to get the textView's text and assign it to a value.***
item.setValue(list, forKeyPath: "summary")
do {
try managedContext.save()
items.append(item)
} catch let error as NSError {
print("Could not save.\(error),\(error.userInfo)")
}
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self,forCellReuseIdentifier: "Cell")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "ToDo")
do {
items = try managedContext.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch.\(error),\(error.userInfo)")
}
}
}
extension ToDoViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = items[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let textView = UITextView(frame: CGRect(x: 0, y: 0, width: cell.frame.size.width, height: cell.frame.size.height))
cell.addSubview(textView)
textView.text = item.value(forKey: "summary") as? String
return cell
}
}
Ok so If my understanding is right you need a new row to be added if they create a new entry into your Core Data. So in your viewWillAppear you're doing a fetch. What I think you need is a:
var fetchResultController : NSFetchedResultsController<YourType>!
Then using this fetch controller you want to do the following when fetching:
private func GetToDoEntries(){
if let appDele = UIApplication.shared.deletgate as? AppDelegate{
let givenContext = appDele.persistantContainer.viewContex
let entryFetchRequest : NSFetchRequest<YourType> = YourType.fetchRequest()
let sortDescriptors = NSSortDescriptor(key: "yourEntrySortKey" , ascending: true)
entryFetchRequest.sortDescriptors = [sortDescriptors]
fetchResultController = NSFetchedResultsController(fetchRequest: entryFetchRequest, managedObjectContext: givenContext, sectionNameKeyPath: nil, cacheName: nil)
fetchResultController.delegate = self
do{
//Gets fetched data based on our criteria
try fetchResultController.performFetch()
if let fetchedEntries = fetchResultController.fetchedObjects{
items = fetchedEntries as? WhateverToCastTo
}
}catch{
print("Error when trying to find entries")
}
}
}
First I'm sorry but I've just written this here is stackOverflow. So what you're doing is using a fetch result controller instead of a traditional search. You are required to have the sort descriptor as well and you can try to get the results and cast them to your items or as a NSManagedObject.
Now we're not done though. Your script needs to inherit from some behaviour. At the top of your class
class ToDoViewController : UIViewController, NSFetchedResultsControllerDelegate
You need the delegate as you can see in the first block of code because we're assigning it. Now we're almost there. You just need some methods to update the table for you and these come with the delegate we just inherited from.
//Allows the fetch controller to update
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
//Allows new additions to be added to the table
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type{
case .insert:
if let _newIndexPath = newIndexPath{
tableView.insertRows(at: [_newIndexPath], with: .fade)
}
case .update:
if let index = indexPath{
tableView.reloadRows(at: [index], with: .fade)
}
default:
budgetEntryTable.reloadData()
}
if let fetchedObjects = controller.fetchedObjects{
items = fetchedObjects as! [NSManagedObject (Or your cast type)]
budgetEntryTable.reloadData()
}
}
//Ends the table adding
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
So there are 3 methods here. The first and second are very self explanatory. They begin and end the updates on your tableView. I'd also recommend that you change the name of your tableView to something other than "tableView" just for clarity.
The method in the middle uses a switch. My example is missing the "Move" and "Delete" options as I didn't required them in my project but you can add them to the switch statement.
The insert is checking the newIndexPath to see if there is one. If so then we add an array of the amount of rows required at the newIndexPath.
The update just checks the current index paths and then reloads the data on them incase you updated your data model.
I hope this is what you were looking for. Good luck! I'll try and help more if you need it but that should get you started.

transitioningDelegate never called after Segue transition

So I'm trying to implement a custom animation as my app transitions from one View Controller to another, but for some reason the animateTransition function in my custom animation class is never called.
For the record, I'm using Xcode 8 and writing in Swift 3. The problem I'm trying to over come, is that the function is never called - I'll sort out the actual animation in the future, for now its
Here is the code in my CustomPresentAnimationController class, which should handle the transition animation...
import UIKit
class CustomPresentAnimationController: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {
let duration = 0.5
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
print("Checking duration")
return duration
}
func animationController(forPresented presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("This ran 1")
return self
}
func presentationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("This ran 2")
return self
}
func animationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("This ran 3")
return self
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
print("It's working!")
guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) else {
return
}
guard let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else {
return
}
let container = transitionContext.containerView
let screenOffDown = CGAffineTransform(translationX: 0, y: -container.frame.height)
let screenOffUp = CGAffineTransform(translationX: 0, y: container.frame.height)
container.addSubview(fromView)
container.addSubview(toView)
toView.transform = screenOffUp
UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: [], animations: {
fromView.transform = screenOffDown
fromView.alpha = 0.5
toView.transform = CGAffineTransform.identity
toView.alpha = 1
}) { (success) in
transitionContext.completeTransition(success)
}
}
}
Here is the code for my ViewController (which both of my View Controllers reference)...
import UIKit
class ViewController: UIViewController, UINavigationControllerDelegate, UIViewControllerTransitioningDelegate {
override func viewDidLoad() {
if transitioningDelegate != nil {
print("Should do something...")
print(transitioningDelegate)
} else {
print("Transitioing Delegate set to nil")
}
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationController?.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
let customPresentAnimationController = CustomPresentAnimationController()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("doing our custom transition")
print(segue.destination)
let destination = segue.destination
destination.transitioningDelegate = customPresentAnimationController
}
}
When I run the code, and click on the button I provided, which links to my seance View Controller, and is set to 'Present Modally', the view changes with the standard transition (slides up from the bottom) - and the following is printed out to Xcode:
Transitioing Delegate set to nil
doing our custom transition
<moduleView.ViewController: 0x7fe427f09a40>
Should do something...
Optional(<moduleView.CustomPresentAnimationController: 0x60800002e980>)
Obviously the first line is just as the first view loads, all the rest shows that my transitionDelegate is set on the Segue destination, and is indeed loaded in as the second view loads, and that the transitionDelegate is set to CustomPresentAnimationController... yet none of the functions in that class are ever called as it never prints anything out from those functions.
Any help appreciated!
Ensure the method signature for implementing the delegate matches the updated Swift 3 syntax.

Swift 3 and NSURLSession issue

Thanks to Apple my iOS 9 Project 'Swift 2.3' is completely unusable with iOS 10's 'Swift 3'...
I fixed almost everything except that I am having issue with using NSURLSession, Xcode is telling me that it has been renamed to URLSession, if I rename it Xcode will tell me:
use of undeclared type URLSession
Foundation is imported.
What is the issue?!
For example I am using it this way...
lazy var defaultSession: URLSession = {
let configuration = URLSessionConfiguration.background(withIdentifier: "reCoded.BGDownload")
configuration.sessionSendsLaunchEvents = true
configuration.isDiscretionary = true
let session = URLSession(configuration: configuration, delegate: self, delegateQueue, queue: nil)
return session
}()
and even with the delegate methods the same issue.
Try using Foundation.URLSession where ever you use URLSession.
/Got it to work/ In some cases try to copy your code somewhere else then remove everything in your class that uses URLSession then type the session methods again and put back your copied code you should be fine.
Update your URLSessin functions with;
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
self.data.append(data as Data)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON()
}
}
I can explain how but by playing around with the code I got this to work in SWIFT 3 after two days of frustration. I guess SWIFT 3 removed a lot of unnecessary words.
let task = Foundation.URLSession.shared.dataTask(with: <#T##URL#>, completionHandler: <#T##(Data?, URLResponse?, Error?) -> Void#>)
Here's where I am right now. It's not perfect but works maybe half of the time.
First, in the class where my URLsession is defined:
import Foundation
class Central: NSObject, URLSessionDataDelegate, URLSessionDelegate, URLSessionTaskDelegate, URLSessionDownloadDelegate {
I don't think all of that is necessary, but there it is. Then here is the function that is called by my background fetch:
func getWebData() {
var defaults: UserDefaults = UserDefaults.standard
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: "myBGconfig")
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
urlString = "https://www.powersmartpricing.org/psp/servlet?type=dayslider"
if let url = URL(string: urlString) {
let rateTask = backgroundSession.downloadTask(with: URL(string: urlString)!)
rateTask.taskDescription = "rate"
rateTask.resume()
}
When the task comes back:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL ) {
if downloadTask.taskDescription == "rate" { // I run 2 web tasks during the session
if let data = NSData(contentsOf: location) {
var return1 = String(data: data as! Data, encoding: String.Encoding.utf8)!
DispatchQueue.global(qos: .userInteractive).asyncAfter(deadline: .now() + 0.2){
var defaults: UserDefaults = UserDefaults.standard
defaults.set(myNumber, forKey: "electricRate") // myNumber is an extract of the text in returned web data
defaults.set(Date(), forKey: "rateUpdate")
defaults.synchronize()
self.calcSetting() //Calls another function defined in the same class. That function sends the user a notification.
let notificationName = Notification.Name("GotWebData")
NotificationCenter.default.post(name: notificationName, object: nil)
} // Closes the Dispatch
}
if session.configuration.identifier == "myBGconfig" {
print("about to invalidate the session")
session.invalidateAndCancel()
}
}
I haven't figured out yet how to kill the session when BOTH tasks have completed, so right now I kill it when either one is complete, with invalidateAndCancel as above.
And finally, to catch errors:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didCompleteWithError: Error?) {
if downloadTask.taskDescription == "rate" {
print("rate download failed with error \(didCompleteWithError)")
}
if downloadTask.taskDescription == "other" {
print("other download failed with error \(didCompleteWithError)")
}
downloadTask.resume() // I'm hoping this retries if a task fails?
}
func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
if let error = error as? NSError {
print("invalidate, error %# / %d", error.domain, error.code)
} else {
print("invalidate, no error")
}
}