Getting access to EKCalendar - swift

In my app, I have a switch that allows the user to put certain events in their agenda. I handle that as such:
#IBAction func putInAgenda(_ sender: UISwitch) {
let store = manager.store
if (sender.isOn){
store.requestAccess(to: EKEntityType.event, completion: {
(accessGranted: Bool, error: Error?) in
if accessGranted == true {
self.eventsHandler.importEventsInAgenda(id)
} else {
DispatchQueue.main.async {
sender.isOn = false
}
}
})
} else {
//
}
shared?.set(sender.isOn, forKey: "putInAgenda")
shared?.synchronize()
}
However, against my expectation, "store.requestAccess" not only requests, but also SETS.
As a result, when the user CANCELS the dialog, the switch switches back (expected) but any consecutive attempt to switch the switch to the ON position is honored with an OFF position, without a new dialog.
What should I do?

A privacy request is only ever asked once. If you detect that it is currently denied, you could either update the UI or prompt the user to go to Settings and turn it on. You can use UIApplication openSettingsURLString and UIApplication openURL to take the user to your app's settings page in the Settings app.

Related

change UIVIew visibility during a long func call

I have a search page with uses OAuth to make an external call to a website for data. Sometimes the data is very quick and others quite long. So I have created a custom object (Searching) to display on screen to indicate that a search is happening (the custom object is just 2 UIImageViews in a UIView)
The problem is that the searching.isHidden = false doesn't actually happen until the end of the func which happens after it gets all the data, even though it is called first. Which is obviously too late.
I tried moving the isHidden to a background thread but get an error saying UIView calls must be on the main thread
I tried moving the display call to its own func with an #escaping callback and then run the search after it returns but it still does not update.
If I remove the search() line it displays properly.
I've also tried forcing a refresh on the object using
self.view.setNeedsLayout()
self.view.setNeedsDisplay()
and it didn't work
class Search {
override func viewDidLoad() {
super.viewDidLoad()
searching.isHidden = true
}
#IBAction func search(_ sender: Any) {
if self.searching.isHidden == false {
self.searching.isHidden = true
}
else {
self.searching.isHidden = false
}
self.view.setNeedsLayout()
self.view.setNeedsDisplay()
//I've also tried using an escaping func to call the isHidden and call back when complete
//self.searching.show() {
//self.view.setNeedsLayout()
//self.view.setNeedsDisplay()
//self.search()
//}
//I've tried an async call
// DispatchQueue.main.async {
// self.search()
// }
}
func search() {
keywordText.resignFirstResponder()
//perform OAuth external search
if results.count > 1 {
searching.isHidden = true
self.performSegue(withIdentifier: "results", sender: nil)
}
return
}
}
On iOS (and MacOS) UI updates don’t get displayed until your code returns and the event loop gets a chance to run. Code that makes UI changes and then immediately does a long-running task on the main thread will not see those changes on-screen until the long-running task completes.
One way to handle this is to change you UI and then use dispatchAsync to trigger the long-running task:
searching.isHidden = false
DispatchQueue.main.async {
// Put your long-running code here.)
}

Detect click when the allow Button in the notification alert permission is tapped

I'm developing this iOS app with SwiftUI.
How I can detect when the user clicks the allow button of the Notification permission alert? It should happen only one time and I need to take the user to a specific tab the first time he tapped the button. Is this possible?
you can add the attribute onclick="location.href='https://google.com';".
but could you post codesnippets? and do you use javascript and php?
I found a solution. To create this behaviour, I simply saved in a UserDefault a Bool value.
func registerForPushNotifications() {
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, _ in
print("Permission granted: \(granted)")
self?.userDefault.set(granted, forKey: "notificationGranted")
guard granted else {
return
}
self?.getNotificationSettings()
}
}
func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
print("Notification settings: \(settings)")
guard settings.authorizationStatus == .authorized else {
return
}
// Saving the value to permit to take the user to the Notification screen only the first time
if (self.userDefault.value(forKey: "permissionPopUpIsShowed") == nil) {
self.userDefault.set(true, forKey: "permissionPopUpIsShowed")
}
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
In the Content View inside the onAppear method you can do this:
if appDelegate.userDefault.bool(forKey: "permissionPopUpIsShowed") == true && appDelegate.userDefault.bool(forKey: "notificationGranted") == true {
tabSelection = .notifications
appDelegate.userDefault.set(false, forKey: "permissionPopUpIsShowed")
}
In this way you will be able to select a new view (through the tabSelection) and obtain this behaviour only once (when the notification permission popUp is showed).

Get Notified When User Starts to Drive (CoreMotion)

I would like to send a notification to the user whenever he/she starts driving using CoreMotion. I can use CoreMotion to see what the user is doing while my app is on like so...
let activityManager = CMMotionActivityManager()
override func viewDidLoad() {
super.viewDidLoad()
activityManager.startActivityUpdates(to: .main) { (activity) in
guard let activity = activity else {
return
}
if activity.automotive {
print("Driving")
}
if activity.stationary {
print("Not Moving")
}
}
}
}
But how would I be able to detect the change to activity.automotive in the background to send a notification to the user even if my app is not on?

Logout on Facebook's Firebase

This question is asked before:
Firebase sign out not working in Swift
Logging a user out with Firebase 3 and Swift still shows the `currentUser`
Firebase - iOS Swift: FIRAuth.auth().signOut() not signing out current user
However all the answers are not working for me. I have VC 1 where the user can login, and in the viewdidappear I have a print that prints the user?.uid. In VC 2 I have a button that should logout the user and goes back to VC 1. The code in VC 2:
#IBAction func logOut(_ sender: UIButton) {
if FIRAuth.auth()?.currentUser != nil {
do {
try! FIRAuth.auth()!.signOut()
} catch let error as NSError {
print(error.localizedDescription)
}
}
FBSDKAccessToken.setCurrent(nil)
loggedIn = false
storedValuesData.setValue(nil, forKey: "savedLoginEmail")
storedValuesData.setValue(nil, forKey: "savedLoginPassword")
jumpToVC1()
}
When I am back at VC 1, when the user pressed the logout button, the print prints again the user's UID. But that user should be logged out, therefore the print should be nil. How can I make sure VC 1 only gets presented when I am sure the user is logged out? I thought completion blocks would be nice, but I am not sure how that would work here...
You don't need completion blocks, as the method is synchronous.
#IBAction func logOut(_ sender: UIButton) {
guard FIRAuth.auth()?.currentUser != nil else {
return
}
do {
try FIRAuth.auth()?.signOut()
FBSDKAccessToken.setCurrent(nil)
loggedIn = false
storedValuesData.setValue(nil, forKey: "savedLoginEmail")
storedValuesData.setValue(nil, forKey: "savedLoginPassword")
jumpToVC1()
} catch let error as NSError {
print(error.localizedDescription)
}
}
AccessToken.current=nil is worked.Because logout is not reverse it.
do {
try Auth.auth().signOut()
AccessToken.current=nil
return true
} catch {
return false
}

xcode swift 3 lock screen remote control not working?

i am trying to catch play/stop/next/prev user action from lock screen when player is active and playing , for some how its not working .
inside class MusicPlayerViewController: BaseViewController
override func viewDidLoad() {
super.viewDidLoad()
do {
UIApplication.shared.beginReceivingRemoteControlEvents()
print("bb> Receiving remote control events\n")
} catch {
print("bb> Audio Session error.\n")
}
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.nextTrackCommand.isEnabled = true
commandCenter.nextTrackCommand.addTarget(self, action: #selector(MusicPlayerViewController.nextTrackCommandSelector))
}
func nextTrackCommandSelector()
{
print("omg")
}
in the log i can see only
bb> Receiving remote control events
also inside AppDelegate.swift has
override func remoteControlReceived(with event: UIEvent?) {
print("remote::")
guard let event = event else {
print("no event\n")
return
}
guard event.type == UIEventType.remoteControl else {
print("received other event type\n")
return
}
switch event.subtype {
case UIEventSubtype.remoteControlPlay:
print("received remote play\n")
case UIEventSubtype.remoteControlPause:
print("received remote pause\n")
case UIEventSubtype.remoteControlTogglePlayPause:
print("received toggle\n")
case UIEventSubtype.remoteControlNextTrack:
print("clicked next \n")
case UIEventSubtype.remoteControlPreviousTrack:
print("clicked Prev \n")
default:
print("received \(event.subtype) which we did not process\n")
}
}
and capabilities
what did i miss ?
A few things:
You're using both the delegate style and MPRemoteCommandCenter style of remote event handling. Pick one, rather than both to see if they're conflicting. Apple recommends the MPRemoteCommandCenter style, but if you're supporting older iOS versions, you may need to stick with the delegate style.
If you do elect to use the delegate style, my recollection is that you must also become the first responder in order to begin receiving remote control events.
Regardless of which style of event handling you choose, you must play audio in your app to let the system know to route events to you. The lock screen (or audio control center) should have your app listed in the "Now Playing" area.
i found the solution
for swift 3 i have to add this
try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, with: [])
try! AVAudioSession.sharedInstance().setActive(true)