How to implement Evenkit in order to request permission - swift

I'm building a mac application which should add a reminder in calendar. The build goes without errors nor warning but when the app launch, I get the following error :
"Reminder failed with error Access to this event store is unauthorized."
I have search the web for the right way to request access to calendar on the mac but have not found any.
I have try to translate the following exemple from ios to mac but it failed: https://github.com/andrewcbancroft/EventTracker/tree/ask-for-permission
Here is my code :
import Cocoa
import EventKit
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSTableViewDataSource, NSTableViewDelegate
{
#IBOutlet weak var window: NSWindow!
var eventStore = EKEventStore()
var calendars: [EKCalendar]?
func applicationDidFinishLaunching(_ aNotification: Notification)
{
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)")
}
}
func applicationWillTerminate(_ aNotification: Notification)
{
// Insert code here to tear down your application
}
}
Thank you for your attention.

You have to call requestAccess(to:completion: on the event store for example
let eventStore = EKEventStore()
eventStore.requestAccess(to: .reminder) { (granted, error) in
if let error = error {
print(error)
return
}
if granted {
// go on managing reminders
}
}

import Cocoa
import EventKit
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSTableViewDataSource, NSTableViewDelegate {
#IBOutlet weak var window: NSWindow!
var eventStore = EKEventStore()
func applicationDidFinishLaunching(_ aNotification: Notification)
{
eventStore.requestAccess(to: .reminder)
{ (granted, error) in
if let error = error
{
print(error)
return
}
if granted
{
let reminder = EKReminder(eventStore: self.eventStore)
reminder.title = "Go to the store and buy milk"
reminder.calendar = self.eventStore.defaultCalendarForNewReminders()
let date : NSDate = NSDate()
let alarm : EKAlarm = EKAlarm (absoluteDate: date.addingTimeInterval(10) as Date)
reminder.addAlarm(alarm)
do
{
try self.eventStore.save(reminder,commit: true)
}
catch let error {print("Reminder failed with error \(error.localizedDescription)")}
}
}
}
func applicationWillTerminate(_ aNotification: Notification)
{
// Insert code here to tear down your application
}
}

Related

How to programmatically save the content written in TextView to a text file when terminating the app?

I want to save the content of the text view when the user closes the app.
I used the following codes to do so, but I cannot get the up-to-date string of the textview when closing the app. So, the produced text file is blank.
How should I access to the NSTextView from AppDelegate to save its content?
ViewController.swift
import Cocoa
class ViewController: NSViewController {
static var textViewString: String = ""
#IBOutlet var textView: NSTextView!{
didSet{
ViewController.textViewString = textView.string
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// start with hidden and show after moving to the main screen
DispatchQueue.main.async {
//keep the window top
self.view.window?.level = .floating
//set up the main display as the display where window shows up
let screens = NSScreen.screens
var pos = NSPoint()
pos.x = screens[0].visibleFrame.midX
pos.y = screens[0].visibleFrame.midY
self.view.window?.setFrameOrigin(pos)
self.view.window?.zoom(self)
self.view.window?.level = .floating
//self.view.window?.backgroundColor = NSColor.white
//stop the user from moving window
self.view.window?.isMovable = false
//disable resizable mode
self.view.window?.styleMask.remove(.resizable)
self.view.window?.setIsVisible(true)
}
//set up font for the reflectionForm
textView.font = NSFont.systemFont(ofSize: 30)
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func saveTextViewString(){
if let documentDirectoryFileURL = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last {
let fileName = "savedText.txt"
let targetTextFilePath = documentDirectoryFileURL + "/" + fileName
do {
try ViewController.textViewString.write(toFile: targetTextFilePath, atomically: true, encoding: String.Encoding.utf8)
print("successfully recorded: \(ViewController.textViewString.description) at \(fileName.utf8CString)")
} catch let error as NSError {
print("failed to write: \(error)")
}
}
}
}
AppDelegate.swift
import Cocoa
#main
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
//save the string in the textview into a text file
ViewController().saveTextViewString()
}
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
}
Thank you for the #jnpdx's comments, I was able to solve this by just declaring ViewController in the AppDelegate by stating var viewController: ViewController!
ViewController.swift
import Cocoa
class ViewController: NSViewController {
#IBOutlet var textView: NSTextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// start with hidden and show after moving to the main screen
DispatchQueue.main.async {
//keep the window top
self.view.window?.level = .floating
//set up the main display as the display where window shows up
let screens = NSScreen.screens
var pos = NSPoint()
pos.x = screens[0].visibleFrame.midX
pos.y = screens[0].visibleFrame.midY
self.view.window?.setFrameOrigin(pos)
self.view.window?.zoom(self)
self.view.window?.level = .floating
//self.view.window?.backgroundColor = NSColor.white
//stop the user from moving window
self.view.window?.isMovable = false
//disable resizable mode
self.view.window?.styleMask.remove(.resizable)
self.view.window?.setIsVisible(true)
}
//set up font for the reflectionForm
textView.font = NSFont.systemFont(ofSize: 30)
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func saveTextViewString(){
if let documentDirectoryFileURL = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last {
let fileName = "savedText.txt"
let targetTextFilePath = documentDirectoryFileURL + "/" + fileName
do {
try textView.string.write(toFile: targetTextFilePath, atomically: true, encoding: String.Encoding.utf8)
print("successfully recorded: \(textView.string.description) at \(fileName.utf8CString)")
} catch let error as NSError {
print("failed to write: \(error)")
}
}
}
}
AppDelegate.swift
import Cocoa
#main
class AppDelegate: NSObject, NSApplicationDelegate {
//connect viewController with ViewController
var viewController: ViewController!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
//save the string in the textview into a text file
viewController.saveTextViewString()
}
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
}

How to bind selector to method inside NSObject [duplicate]

This question already has an answer here:
NSStatusItem in NSStatusBar, action selector method not responding
(1 answer)
Closed 2 years ago.
In the macOS swiftui project I have the following code
import Cocoa
import SwiftUI
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
var statusItem: StatusItem = StatusItem()
func applicationDidFinishLaunching(_ aNotification: Notification) {
}
#objc public func statusBarButtonClicked(sender: NSStatusBarButton) {
let event = NSApp.currentEvent!
if event.type == NSEvent.EventType.rightMouseUp {
print("Right click! (AppDelegate)")
} else {
print("Left click! (AppDelegate)")
}
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
import Cocoa
class StatusItem : NSObject {
private let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
override init() {
super.init()
self.item.button?.title = "title"
self.item.button?.action = #selector(self.statusBarButtonClicked(sender:))
self.item.button?.sendAction(on: [.leftMouseUp, .rightMouseUp])
}
#objc public func statusBarButtonClicked(sender: NSStatusBarButton) {
let event = NSApp.currentEvent!
if event.type == NSEvent.EventType.rightMouseUp {
print("Right click! (NSObject)")
} else {
print("Left click! (NSObject)")
}
}
}
But when I click NSStatusBarButton it prints "Left click! (AppDelegate)" and "Right click! (AppDelegate)" to console.
Why does it happen? And how to make it call statusBarButtonClicked method defined in StatusItem class?
Setting the button's action is only one half of what you need to do. You also need to specify a target. Add
self.item.button?.target = self
and I believe you will get the result you are looking for.
What's happening is action specifies the selector to invoke and target specifies the object on which to invoke it.

Xcode Project Setting for CGEventTap?

A while ago, I created a extremely simple Xcode project to test CGEventTap, and it works perfectly fine when I run from Xcode. The code is at the bottom.
However, if I create a new project on Xcode, paste the exactly the same code below, and run from Xcode, I get "Failed to create event tap".
Is there a project setting that I need to change in order to get CGEventTap working? I even tried to copy and paste info.plist from the old testing project to the new one.
I'm very puzzled. Thank you for your help!
// ViewController.swift
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
func myCGEventCallback(proxy : CGEventTapProxy, type : CGEventType, event : CGEvent, refcon : UnsafeMutableRawPointer?) -> Unmanaged<CGEvent>? {
if type == .keyDown || type == .keyUp || type == .flagsChanged {
let keyCode = event.getIntegerValueField(.keyboardEventKeycode)
print(keyCode)
}
return Unmanaged.passRetained(event)
}
let eventMask = (1 << CGEventType.keyDown.rawValue) | (1 << CGEventType.keyUp.rawValue) | (1 << CGEventType.flagsChanged.rawValue)
guard let eventTap = CGEvent.tapCreate(tap: .cgSessionEventTap, place: .headInsertEventTap, options: .defaultTap, eventsOfInterest: CGEventMask(eventMask), callback: myCGEventCallback, userInfo: nil) else {
debugPrint("Failed to create event tap")
exit(1)
}
let runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0)
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, CFRunLoopMode.commonModes)
CGEvent.tapEnable(tap: eventTap, enable: true)
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
// AppDelegate.swift
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
The answer was to uncheck Sandbox from capabilities.

Parse PFUser not registering subclass

I am trying to use Parse PFUser in a software for OSX desktop. When I try to use it PFUser.query() it gives a message: Failed to set (contentViewController) user defined inspected property on (NSWindow): The class PFUser must be registered with registerSubclass before using Parse.
It is happening without registering the class.
I tried it registering the class this way: PFUser.registerSubclass() but it still doesn't work.
I will use the default PFUser without adding any fields to it, so I don't need to create a custom class to be my PFUser.
I tried to use PFUser.enableAutomaticUser() without success
Code below:
AppDelegate.swift
import Cocoa
import Parse
import Bolts
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
let APP_ID = "app_id"
let CLIENT_KEY = "client_key"
let SERVER = "https://parseserver.com/"
func applicationDidFinishLaunching(_ aNotification: Notification) {
PFUser.registerSubclass()
let configuracaoParse = ParseClientConfiguration {
$0.applicationId = self.APP_ID
$0.clientKey = self.CLIENT_KEY
$0.server = self.SERVER
}
Parse.initialize(with: configuracaoParse)
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
ViewController.swift
import Cocoa
import Parse
class ViewController: NSViewController {
#IBOutlet weak var emailTextField: NSTextField!
#IBOutlet weak var senhaSecureTextField: NSSecureTextField!
override func viewDidLoad() {
super.viewDidLoad()
contaUsuarios()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
#IBAction func entrarButtonClicked(_ sender: NSButton) {
}
func contaUsuarios() {
let query = PFUser.query()
query?.countObjectsInBackground(block: {
(count, error) -> Void in
let numeroUsers = Int(UInt32(count))
if numeroUsers > 0 {
}
print(numeroUsers)
})
}
}
Reading some content on the internet I discovered that in OSX the ViewController is launched before the AppDelegate finishes loading, so I initialized the Parse connection and subclassing in the ViewController's viewDidLoad instead of AppDelegate and it is working just fine.

FBLoginManager undeclared type

I installed FacebookSDK using Cocoapods, according to Terminal, I have installed FacebookSDK 4.8.0 (CoreKit, ShareKit and LoginKit), I imported the .h files in my BH-File.h, and already initialized everything in my AppDelegate.
For some reason, when trying to log in using a custom button, when I initialize FBLoginManager, I get an error Use of undeclared type "FBLoginManager".
this is my code
if (FBSDKAccessToken.currentAccessToken() == nil)
{
let fbLoginManager : FBSDKLoginManager =
fbLoginManager.logInWithReadPermissions(["public_profile", "email"], fromViewController: self, handler: { (loginResult, error) -> Void in
if error == nil {
print (FBSDKAccessToken.currentAccessToken().tokenString)
}
else {
print ("ERROR*****: \(error)")
}
})
}
What fixed to me was adding import FBSDKCoreKit and FBSDKLoginKit to my class, for some reason is not enough adding it in the BH-file.h
Try something like this, I just checked the code and it works (it's not exactly what you're looking for but I'm sure you can modify it as needed)
import UIKit
import FBSDKCoreKit
import FBSDKLoginKit
class ProfileViewController: UIViewController,FBSDKLoginButtonDelegate {
// #IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var nextButton: UIButton!
#IBOutlet weak var fbLoginButton: FBSDKLoginButton!
override func viewDidLoad() {
super.viewDidLoad()
self.fbLoginButton.delegate = self
self.fbLoginButton.readPermissions = ["public_profile"]
self.fbLoginButton.publishPermissions = ["publish_actions"]
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "fbProfileChanged:",
name: FBSDKProfileDidChangeNotification,
object: nil)
FBSDKProfile.enableUpdatesOnAccessTokenChange(true)
// If we have a current Facebook access token, force the profile change handler
if ((FBSDKAccessToken.currentAccessToken()) != nil)
{
self.fbProfileChanged(self)
} }
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
//facebooks functions
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!) {
if (error != nil)
{
print( "\(error.localizedDescription)" )
}
else if (result.isCancelled)
{
// Logged out?
print( "Login Cancelled")
}
else
{
// Logged in?
print( "Logged in, segue now")
self.performSegueWithIdentifier("showHome", sender: self)
}
}
func loginButtonDidLogOut(loginButton: FBSDKLoginButton!) {
}
//see bitfountain
func fbProfileChanged(sender: AnyObject!) {
let fbProfile = FBSDKProfile.currentProfile()
if (fbProfile != nil)
{
// Fetch & format the profile picture
let strProfilePicURL = fbProfile.imagePathForPictureMode(FBSDKProfilePictureMode.Square, size: imageView.frame.size)
let url = NSURL(string: strProfilePicURL, relativeToURL: NSURL(string: "http://graph.facebook.com/"))
let imageData = NSData(contentsOfURL: url!)
let image = UIImage(data: imageData!)
self.nameLabel.text = fbProfile.name
self.imageView.image = image
self.nameLabel.hidden = false
self.imageView.hidden = false
self.nextButton.hidden = false
}
else
{
self.nameLabel.text = ""
self.imageView.image = UIImage(named: "")
self.nameLabel.hidden = true
self.imageView.hidden = true
}
}
#IBAction func nextButtonPressed(sender: UIButton) {
self.performSegueWithIdentifier("showHome", sender: self)
}
}