EKCalendar title in iOS12 is blank - iphone

In iOS 12 I'm trying to list the calendars. I can print the calendar ids, but the titles are all blank. What am I doing wrong?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let status = EKEventStore.authorizationStatus(for: EKEntityType.event)
switch (status) {
case EKAuthorizationStatus.notDetermined:
EKEventStore().requestAccess(to: .event, completion: {
(granted: Bool, error: Error?) in
if granted != true {
print("Access not granted")
}
})
case EKAuthorizationStatus.authorized:
print("Access granted")
case EKAuthorizationStatus.restricted, EKAuthorizationStatus.denied:
print("Access restricted or denied")
}
print("Calendars:")
for c in EKEventStore().calendars(for: EKEntityType.event) {
print(" \(c.calendarIdentifier):\(c.title)")
}
}

I'm not sure why, but this code shows event titles in iOS 12 Simulator.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let status = EKEventStore.authorizationStatus(for: EKEntityType.event)
let eventStore = EKEventStore() //<-
switch (status) {
case EKAuthorizationStatus.notDetermined:
eventStore.requestAccess(to: .event, completion: {
(granted: Bool, error: Error?) in
if granted != true {
print("Access not granted")
}
})
case EKAuthorizationStatus.authorized:
print("Access granted")
case EKAuthorizationStatus.restricted, EKAuthorizationStatus.denied:
print("Access restricted or denied")
}
print("Calendars:")
for c in eventStore.calendars(for: EKEntityType.event) {
print(" \(c.calendarIdentifier):\(c.title)",c)
}
}
Maybe, you need to keep strong reference to the instance of EKEventStore while accessing EKCalendar properties.

Related

Image gallery not show after request access permission

i try to show image gallery after user permission to allow all photos, but the gallery is not showing. but when i back to previous controller and navigate back, the gallery show up. but that's not what i want, i want after user allow the image show up.
this my setup
private var allPhotos: [PHAsset] = []
override func viewDidLoad() {
super.viewDidLoad()
PHPhotoLibrary.shared().register(self)
setupCollectionView()
checkPhotoLibraryPermission()
bindViewModel()
}
deinit {
PHPhotoLibrary.shared().unregisterChangeObserver(self)
}
private func bindViewModel() {
let dataSource = Observable.just(allPhotos)
dataSource.asObservable()
.bind(to: collectionView.rx.items(cellIdentifier: GalleryCollectionViewCell.cellId, cellType: GalleryCollectionViewCell.self)) { row, asset, cell in
let imageRequestOptions = PHImageRequestOptions()
imageRequestOptions.resizeMode = .exact
self.imageManager.requestImageDataAndOrientation(for: asset, options: imageRequestOptions) { imageData, _, orientation, info in
guard let imageData = imageData else { return }
cell.setup(imageData: imageData)
}
}.disposed(by: disposedBag)
collectionView.rx.itemSelected
.subscribe(onNext: { [weak self] indexPath in
guard let strongSelf = self else { return }
let asset = strongSelf.allPhotos[indexPath.row]
asset.requestContentEditingInput(with: PHContentEditingInputRequestOptions()) { editingInput, info in
guard let path = editingInput?.fullSizeImageURL?.path.replacingOccurrences(of: "HEIC", with: "PNG") else { return }
self?.imageManager.requestImageDataAndOrientation(for: asset, options: self?.imageRequestOptions) { imageData, _, orientation, info in
guard let imageData = imageData else { return }
self?.goToCropImage(from: imageData, and: path.lastPathComponent)
}
}
}).disposed(by: disposedBag)
}
private func fetchAllPhotos() {
let allPhotosOptions = PHFetchOptions()
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let fetchResult = PHAsset.fetchAssets(with: .image, options: allPhotosOptions)
allPhotos = fetchResult.objects(at: IndexSet(0..<fetchResult.count))
}
private func checkPhotoLibraryPermission() {
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized:
fetchAllPhotos()
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
case .denied, .restricted :
//handle denied status
gotoAppSettings()
case .notDetermined:
// ask for permissions
PHPhotoLibrary.requestAuthorization { status in
switch status {
case .authorized:
self.fetchAllPhotos()
case .denied, .restricted:
// as above
self.gotoAppSettings()
case .notDetermined:
// won't happen but still
break
case .limited:
break
#unknown default:
fatalError("Failed to get user permission to access photo")
}
}
case .limited:
fetchAllPhotos()
#unknown default:
fatalError("Failed to get user permission to access photo")
}
}
func photoLibraryDidChange(_ changeInstance: PHChange) {
let allPhotosOptions = PHFetchOptions()
let fetchResult = PHAsset.fetchAssets(with: .image, options: allPhotosOptions)
DispatchQueue.main.async {
self.allPhotos = fetchResult.objects(at: IndexSet(0..<fetchResult.count))
self.collectionView.reloadData()
}
}
I already try to to reload collectionView but it still not show up.
The way that UICollectionView.rx.items works is that it observes its dataSource. When the dataSource emits a new array, the items operator will reload the collection view and call its closure for each item.
Since you are using just as your data source, only one array is emitted and the collection view never changes. You have to tie the source to the change observer to get it to work. Here is a working example:
extension PhotosViewController { // a UICollectionViewController
func connect(disposeBag: DisposeBag) {
// initial fetch result
let allPhotosOptions = PHFetchOptions()
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
let initialFetchResult = PHAsset.fetchAssets(with: allPhotosOptions)
let assets = PHPhotoLibrary.shared().rx.registerChangeObserver()
// when a change is observed, we need to update the fetchResult
.scan(initialFetchResult) { oldResult, change in
guard let changes = change.changeDetails(for: oldResult) else { return oldResult }
return changes.fetchResultAfterChanges
}
// but first send the initial asset fetch to the collection view
.startWith(initialFetchResult)
// and get the assets out of the fetch result.
.map { $0.objects(at: IndexSet(0 ..< $0.count)) }
collectionView.dataSource = nil
assets
.observe(on: MainScheduler.instance)
.bind(to: collectionView.rx.items(cellIdentifier: "GridViewCell", cellType: GridViewCell.self)) { _, asset, cell in
cell.configure(asset: asset)
}
.disposed(by: disposeBag)
}
}
extension Reactive where Base: PHPhotoLibrary {
// not actually needed, but I provided it as an example.
static func requestAuthorization() -> Observable<PHAuthorizationStatus> {
Observable.create { observer in
Base.requestAuthorization { status in
observer.onNext(status)
observer.onCompleted()
}
return Disposables.create()
}
}
// this sets up the change observer. Note, your VC isn't the observer.
func registerChangeObserver() -> Observable<PHChange> {
Observable.create { [base] observer in
let changeObserver: RxPhotoLibraryChangeObserver = .init(observer: observer)
base.register(changeObserver)
return Disposables.create { base.unregisterChangeObserver(changeObserver) }
}
}
}
// this is the change observer used in the above.
final class RxPhotoLibraryChangeObserver: NSObject, PHPhotoLibraryChangeObserver {
let observer: AnyObserver<PHChange>
init(observer: AnyObserver<PHChange>) {
self.observer = observer
}
func photoLibraryDidChange(_ changeInstance: PHChange) {
observer.onNext(changeInstance)
}
}

Perform segue after notification access has been granted

I would like to know how to perform a modal segue after the remote notification access has been granted from the dialog box. I have set up my remote notification in the app delegate.
func registerANSForApplication(_ application: UIApplication,withBlock block: #escaping (_ granted:Bool) -> (Void)){
InstanceID.instanceID().instanceID { (result, error) in
if let error = error {
print("Error fetching remote instange ID: \(error)")
} else if let result = result {
print("Remote instance ID token: \(result.token)")
AppDelegate.isToken = result.token
}
}
let current = UNUserNotificationCenter.current()
let options : UNAuthorizationOptions = [.sound, .badge, .alert]
current.requestAuthorization(options: options) { (granted, error) in
guard granted else{
return
}
if error != nil{
print(error?.localizedDescription ?? "")
}else{
Messaging.messaging().delegate = self
current.delegate = self
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
}
}
Then, in my view controller, I have this code:
let appDelegate = UIApplication.shared.delegate as!
appDelegate.registerANSForApplication(UIApplication.shared) { (granted) -> (Void) in
self.performSegue(withIdentifier: "MembershipVC", sender: nil)
}
The problem is whether the user allows or denies the access to notification, the segue is not executed.
Thank you for your help.
You have to call the block parameter
Replace
current.requestAuthorization(options: options) { (granted, error) in
guard granted else{
return
}
if error != nil{
print(error?.localizedDescription ?? "")
}else{
Messaging.messaging().delegate = self
current.delegate = self
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
}
with
current.requestAuthorization(options: options) { (granted, error) in
if error != nil {
print(error?.localizedDescription ?? "")
block(false)
} else {
Messaging.messaging().delegate = self
current.delegate = self
DispatchQueue.main.async {
application.registerForRemoteNotifications()
block(granted)
}
}
}

Cannot delete EKCalendar, get Error Domain=EKErrorDomain Code=72

I would like to delete an EKCalendar. This works fine on iOS devices and simulators, however trying to get this working on Catalina is problematic. Whatever I try I get this:
Error Domain=EKErrorDomain Code=72 "Can't delete a calendar if doing
so would leave the account with no calendars which are eligible to be
the default scheduling calendar."
UserInfo={NSLocalizedDescription=Can't delete a calendar if doing so
would leave the account with no calendars which are eligible to be the
default scheduling calendar.}
Any pointers? I have been chasing this for weeks! Thanks!
I have been granted permission for both Calendars and Reminders:
import UIKit
import EventKit
class ViewController: UIViewController {
let eventStore = EKEventStore()
func deleteCal (eventStoreToUse: EKEventStore) {
let calsArray = eventStoreToUse.calendars(for: .event)
for cals in calsArray {
print (cals.title)
if cals.title == "Gaps2" || cals.title == "Done" {
do { try _ = eventStoreToUse.removeCalendar(cals, commit: true)
} catch {
print ("Error \(error)")
}
} else {
print ("Did not delete \(cals.title)")
}
}
}
func askAccess() {
switch EKEventStore.authorizationStatus(for: .event) {
case .authorized:
print ("Calendars Access Granted")
case .denied:
print("Access denied")
case .notDetermined:
eventStore.requestAccess(to: .event, completion:
{[weak self] (granted: Bool, error: Error?) -> Void in
if granted {
print("Granted")
self?.deleteCal(eventStoreToUse: (self?.eventStore)!)
} else {
print("Access denied")
}
})
default:
print("Case default")
}
switch EKEventStore.authorizationStatus(for: .reminder) {
case .authorized:
print ("Reminders Access Granted")
case .denied:
print("Access denied")
case .notDetermined:
eventStore.requestAccess(to: .event, completion:
{[weak self] (granted: Bool, error: Error?) -> Void in
if granted {
print("Granted")
self?.deleteCal(eventStoreToUse: (self?.eventStore)!)
} else {
print("Access denied")
}
})
default:
print("Case default")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
askAccess()
deleteCal(eventStoreToUse: eventStore)
}
}

What is the correct way to log in with facebook on firebase? swift

When I log in with a facebook account in a view, I pass it a second view, in the second view I want a fetch query but in the view log I get permission denied and I dont see the info.
I have a normal firebase account, application test facebook.
this is the code view log in
#IBAction func InicioSesionFacebook(_ sender: Any)
{
esperaSesion.isHidden = false
esperaSesion.startAnimating()
let fbLoginManager = FBSDKLoginManager()
fbLoginManager.logIn(withReadPermissions: ["public_profile", "email"], from: self) { (result, error) in
if let error = error {
print("Failed to login: \(error.localizedDescription)")
self.esperaSesion.stopAnimating()
return
}
guard let accessToken = FBSDKAccessToken.current() else {
print("Failed to get access token")
self.esperaSesion.stopAnimating()
return
}
let credential = FacebookAuthProvider.credential(withAccessToken: accessToken.tokenString)
// Perform login by calling Firebase APIs
Auth.auth().signIn(with: credential, completion: { (user, error) in
if let error = error
{
self.esperaSesion.stopAnimating()
print("Login error: \(error.localizedDescription)")
let alertController = UIAlertController(title: "Login Error", message: error.localizedDescription, preferredStyle: .alert)
let okayAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(okayAction)
self.present(alertController, animated: true, completion: nil)
return
}
else
{
let fbloginresult : FBSDKLoginManagerLoginResult = result!
if (result?.isCancelled)!
{
return
}
else
{
// Present the main view
self.esperaSesion.stopAnimating()
if let viewController = self.storyboard?.instantiateViewController(withIdentifier: "NavigationMasterController")
{
UIApplication.shared.keyWindow?.rootViewController = viewController
self.dismiss(animated: true, completion: nil)
}
}
}
})
}
}
this is the code in the second view, a query
import FirebaseAuth
import FirebaseDatabase
import FBSDKLoginKit
var refDB: DatabaseReference!
override func viewDidLoad()
{
super.viewDidLoad()
refDB = Database.database().reference()
CerrarSesion.layer.cornerRadius = 8
imagenPerfil.layer.cornerRadius = imagenPerfil.frame.height/2
imagenPerfil.clipsToBounds = true
verDatos()
// Do any additional setup after loading the view.
}
func verDatos()
{
let userID = Auth.auth().currentUser?.uid
refDB.child("users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let nombre = value?["nombre"] as? String ?? ""
let apellido = value?["apellido"] as? String ?? ""
self.nombreUsuario.text = nombre
self.apellidoUsuario.text = apellido
// ...
}) { (error) in
print(error.localizedDescription)
}
}
and the button log out
#IBAction func CerrarSesion(_ sender: Any)
{
do
{
try Auth.auth().signOut()
self.view.window?.rootViewController?.dismiss(animated: true, completion: borrarUserDefaults)
}
catch let error as NSError
{
print (error.localizedDescription)
}
}
how is the correct form for log out when I logged in with facebook account?
You can check out my YouTube Tutorial on this exact topic !
https://www.youtube.com/watch?v=BfwNf-W-R4U
The version of the Facebook API that you are using is dated. The Login function should look something like this
let loginManager = LoginManager()
loginManager.logIn(readPermissions: [.publicProfile], viewController: self) {loginResult in
switch loginResult {
case .failed(let error):
print("error: \(error)")
case .cancelled:
print("User cancelled login.")
case .success(let grantedPermissions, let declinedPermissions, let accessToken):
print(grantedPermissions)
print(declinedPermissions)
fbAccessToken = accessToken
let credential = FacebookAuthProvider.credential(withAccessToken: (fbAccessToken?.authenticationToken)!)
Auth.auth().signIn(with: credential) { (user, error) in
if let error = error {
print(error)
return
}
currentUser = Auth.auth().currentUser
moveToHomeScreen()
print("Logged in!")
}
}
}
I think that you are getting a permissions error because the parameter name from the AccessToken changed and you are passing the wrong value. (Sorry I cant recall what the change was).
If you are following the Facebook API instructions on the facebook developer portal they are horrendously out of date iOS 9 I think.

how do I perform segue after log in with facebook account?

Currently I am attempting to perform a segue to a second view controller after a user logs in with Facebook using firebase
I was able to sort of get this to work. My problem is I have to actually log-in twice before the Segue is activated. Any suggestions?
see my CODE below
private var fbLoginSuccess = false //This is gobal
override func viewDidAppear(_ animated: Bool) {
if (FBSDKAccessToken.current() != nil && fbLoginSuccess == true)
{
performSegue(withIdentifier: "Home", sender: self)
}
}
#IBAction func facebookLogin(sender: UIButton) {
let facebookLogin = FBSDKLoginManager()
facebookLogin.logIn(withReadPermissions: ["public_profile", "email"], from: self, handler: {
(facebookResult, facebookError) -> Void in
if facebookError != nil {
print("Facebook login failed. Error \(String(describing: facebookError))")
} else if (facebookResult?.isCancelled)! {
print("Facebook login was cancelled.")
} else {
let credential = FacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
Auth.auth().signIn(with: credential) { (user, error) in
if error != nil {
print("Login failed. \(String(describing: error))")
} else {
fbLoginSuccess = true
print("Logged in!")
if (facebookResult?.grantedPermissions.contains("email"))! {
}
}
}
}
})
}
It appears that you're only calling performSegue(withIdentifier:) in viewDidAppear. If you want the segue to occur after signing in, then you need to include it there.
let facebookLogin = FBSDKLoginManager()
facebookLogin.logIn(withReadPermissions: ["public_profile", "email"], from: self, handler: {
(facebookResult, facebookError) -> Void in
if facebookError != nil {
print("Facebook login failed. Error \(String(describing: facebookError))")
} else if (facebookResult?.isCancelled)! {
print("Facebook login was cancelled.")
} else {
let credential = FacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
Auth.auth().signIn(with: credential) { (user, error) in
if error != nil {
print("Login failed. \(String(describing: error))")
} else {
fbLoginSuccess = true
print("Logged in!")
if (facebookResult?.grantedPermissions.contains("email"))! {
}
performSegue(withIdentifier: "Home", sender: self)
}
}
}
})