* Add API boilerplate for new calendar and reminder Addresses iOS 17 API changes * Abstract calendar/reminder into event manager * Fix obsolete API mark and revise message * Squashed commit of the following: commit 5ecef99e3b2ef04a2a05b34509e8a449031d92c4 Author: Jevon Mao <woodburyjevonmao@gmail.com> Date: Tue Aug 8 20:13:01 2023 -0400 Add documentation snippet for permission managers commit44e3f41313Author: Jevon Mao <woodburyjevonmao@gmail.com> Date: Mon Aug 7 14:41:46 2023 -0400 Implement custom permission description color (#137) commitf3ed32ce20Author: Jevon Mao <woodburyjevonmao@gmail.com> Date: Mon Aug 7 12:58:42 2023 -0400 Disable stale check CICD * Erase type for custom foreground color * Add NSLog warning deprecated EventKit permissisons * Remove available limitation
95 lines
3.8 KiB
Swift
95 lines
3.8 KiB
Swift
//
|
|
// JMPermissionsView.swift
|
|
//
|
|
//
|
|
// Created by Jevon Mao on 1/30/21.
|
|
//
|
|
|
|
import SwiftUI
|
|
import Introspect
|
|
|
|
@available(iOS 13.0, tvOS 13.0, *)
|
|
@usableFromInline struct ModalViewWrapper<Body: View>: View, CustomizableView {
|
|
//store contains static configurations and customizations
|
|
@usableFromInline var store: PermissionStore
|
|
//schemaStore contains dynamically computed properties, and internal methods/properties
|
|
@usableFromInline var schemaStore: PermissionSchemaStore
|
|
//Keep track of whether modal as already been shown for 1st time
|
|
@State var isModalNotShown = true
|
|
@usableFromInline var showing: Binding<Bool>
|
|
@usableFromInline var bodyView: Body
|
|
//Placeholder to make sure permissionsToAsk only get computed value once
|
|
//Otherwise, the list of permissions will change while the modal is still open, which is not good
|
|
var _permissionsToAsk: [PermissionManager]?
|
|
var permissionsToAsk: [PermissionManager] {
|
|
guard _permissionsToAsk == nil else {
|
|
return _permissionsToAsk!
|
|
}
|
|
return schemaStore.undeterminedPermissions
|
|
}
|
|
var shouldShowPermission: Binding<Bool>{
|
|
Binding(get: {
|
|
//configStore.autoCheckAuth is added in newer version. autoCheckModalAuth is backward compatibility
|
|
if (store.configStore.autoCheckAuth ||
|
|
(store.autoCheckModalAuth || store.autoCheckAlertAuth)) &&
|
|
//Prevent modal from unwanted dismiss while still presented
|
|
isModalNotShown {
|
|
//underterminedPermissions.isEmpty => No askable permissions => should not show modal
|
|
return !permissionsToAsk.isEmpty
|
|
}
|
|
//Always show the modal regardless of permission status
|
|
return true
|
|
}, set: {_ in})
|
|
}
|
|
@usableFromInline init(for bodyView: Body,
|
|
showing: Binding<Bool>,
|
|
store: PermissionStore,
|
|
permissionsToAsk: [PermissionManager]?=nil) {
|
|
self.bodyView = bodyView
|
|
self.showing = showing
|
|
self._permissionsToAsk = permissionsToAsk
|
|
self.store = store
|
|
self.schemaStore = PermissionSchemaStore(store: store,
|
|
permissionViewStyle: .modal)
|
|
}
|
|
|
|
@usableFromInline var body: some View {
|
|
Group {
|
|
bodyView
|
|
.sheet(isPresented: showing.combine(with: shouldShowPermission), content: {
|
|
ModalView(showModal: showing)
|
|
//Possible nil, to account for backward compatibility
|
|
.onAppear(perform: store.onAppear ?? store.configStore.onAppear)
|
|
.onDisappear(perform: store.onDisappear ?? store.configStore.onDisappear)
|
|
//Writing duplicate onAppear and OnDisappear is actually less lines of code
|
|
.onAppear{isModalNotShown=false}
|
|
.onDisappear{showing.wrappedValue = false; isModalNotShown=true}
|
|
})
|
|
}
|
|
.withEnvironmentObjects(store: store, permissionStyle: .modal)
|
|
|
|
|
|
|
|
}
|
|
//if DEBUG to ensure these functions are never used in production. They are for unit testing only.
|
|
#if DEBUG
|
|
func testCallOnAppear(){
|
|
guard let onAppear = store.configStore.onAppear else {return}
|
|
onAppear()
|
|
}
|
|
func testCallOnDisappear(){
|
|
guard let onDisappear = store.configStore.onDisappear else {return}
|
|
onDisappear()
|
|
}
|
|
#endif
|
|
|
|
}
|
|
//Extension Binding wrapper for Binding booleans
|
|
@available(iOS 13.0, tvOS 13.0, *)
|
|
extension Binding where Value == Bool{
|
|
func combine(with value2: Binding<Bool>) -> Binding<Bool>{
|
|
//Combine two Binding Bool conditions with AND operator
|
|
return self.wrappedValue && value2.wrappedValue ? .constant(true) : .constant(false)
|
|
}
|
|
}
|