我有一个类,表示从事件存储中检索的Calendar项(模型)。我尚未为AppDelegate或ViewControllers实现任何委托。
我在此类中的所有方法都是静态函数-主要原因是我可以从AppDelegate或VC中“看到”它们。我怀疑:
1)我需要使它成为单例-其唯一功能是从eventStore检索日历项并将其发布到UI
2)学习如何更好地编码-也许在AppDelegate和VC中创建类的实例
这对我来说仍然很模糊-不确定发布代码是否有帮助,但是该类有一堆“静态函数.... doSomething(){...}” 和由AppDelegate和VC称为“ ClassName.doSomething()...”
我准备重构Class代码,以为单例可以工作-也许一切都很好...
已编辑:添加代码:
import Foundation
import EventKit
class Calendars: NSObject {
enum calendarAuthState {
case restricted
case authorized
case denied
case notDetermined
}
struct Calendar {
var id: String
var color: NSColor
var title: String
var isUserActive: Bool
var events: [EventItem]
}
struct EventItem {
var originalStartDate: Date
var date: String
var title: String
var isAllDayEvent: Bool
}
static var calendarState: calendarAuthState = .notDetermined
static var eventStore = EKEventStore()
static var currentCalendars = [Calendar]()
//MARK: Check Calendar Authorization Status
static func calendarAuthorizationStatus() {
let status = EKEventStore.authorizationStatus(for: .event)
switch (status) {
case EKAuthorizationStatus.notDetermined:
// This happens on first-run
calendarState = .notDetermined
case EKAuthorizationStatus.authorized:
calendarState = .authorized
case EKAuthorizationStatus.restricted:
self.requestAccessToCalendar()
calendarState = .restricted
case EKAuthorizationStatus.denied:
self.requestAccessToCalendar()
calendarState = .denied
}
}
static func requestAccessToCalendar() {
self.eventStore.requestAccess(to: EKEntityType.event, completion: {
(accessGranted: Bool, error: Error?) in
if accessGranted == true {
DispatchQueue.main.async(execute: {
self.calendarState = .authorized
})
} else {
DispatchQueue.main.async(execute: {
self.calendarState = .denied
})
}
})
}
//MARK: Do the two below
static func createMenuFromCalendars() {
guard calendarState == .authorized else {
return
}
let calendars = self.returnCalendars()
guard calendars.count >= 0 else {
return
}
self.addCalendarsToMenuItems(from: calendars)
}
//MARK: First, return the calendar titles from the Store
static func returnCalendars() -> [Calendar] {
guard self.calendarState == .authorized else {
return[]
}
let calendars = self.eventStore.calendars(for: .event)
for calendar in calendars {
self.currentCalendars.append(Calendar(id: calendar.calendarIdentifier, color: calendar.color, title: calendar.title, isUserActive: false, events: []))
}
return self.currentCalendars
}
//MARK: Next, send those to the Menu for MenuItem creation
static func addCalendarsToMenuItems(from calendars:[Calendar]) {
let appDelegate = NSApplication.shared.delegate as! AppDelegate
let appMainMenu = NSApp.mainMenu
if let calendarMenu = appMainMenu?.item(withTitle: "Calendars") {
let calendarSubMenu = calendarMenu.submenu
for calendar in calendars {
let menuItem = calendarSubMenu?.addItem(withTitle: calendar.title, action: #selector(appDelegate.actionFromSelectedCalendar) , keyEquivalent: "")
menuItem?.isEnabled = true
menuItem?.state = .off
menuItem?.target = appDelegate.self
menuItem?.toolTip = calendar.id
}
}
}
class func retrieveCalendarEvents() {
guard self.calendarState == .authorized || !(self.currentCalendars.isEmpty) else {
return
}
let startDate = Date()
let endDate = Date(timeIntervalSinceNow: 4*24*3600)
var activeCalendars = findUserActiveCalendars(in: currentCalendars)
//need to flush the events at this stage or they'll pile
guard !((activeCalendars?.isEmpty)!) else {
return
}
var eventCalendar = [EKCalendar]()
for dayBookCalendar in activeCalendars! {
// much of the risk here is unwrapping optionals unsafely!!!!! - refactor this and other please
eventCalendar.append(self.eventStore.calendar(withIdentifier: dayBookCalendar.id)!)
let eventPredicate = eventStore.predicateForEvents(withStart: startDate, end: endDate, calendars: eventCalendar)
let returnedEvents = eventStore.events(matching: eventPredicate)
let calendarIndex = findCalendarIndex(by: dayBookCalendar.id, in: currentCalendars)
for event in returnedEvents {
let eventItems = eventItem(from: event)
currentCalendars[calendarIndex!].events.append(eventItems)
}
}
}
//MARK: Helper methods and stuff
static func changeUserCalendarState(with id:String, state:Bool) {
guard !(currentCalendars.isEmpty) else {
return
}
let calendarIndex = findCalendarIndex(by: id, in:self.currentCalendars)
if let calendarIndex = calendarIndex {
currentCalendars[calendarIndex].isUserActive = !state
retrieveCalendarEvents()
}
}
static func findCalendarIndex(by id:String, in calendarArray: [Calendar]) -> Int? {
return calendarArray.index(where: {$0.id == id})
}
static func findUserActiveCalendars(in calendarArray: [Calendar]) -> [Calendar]? {
return calendarArray.filter({$0.isUserActive == true})
}
// static func flushEventsFromCalendar(in calendarArray: inout [Calendar]) {
// calendarArray.map({$0.events.removeAll()})
// }
static func eventItem(from events:EKEvent) -> EventItem {
return EventItem(originalStartDate: events.startDate, date:eventTime(from: events.startDate), title: events.title!, isAllDayEvent: events.isAllDay)
}
static func parseCalendarEvents(from events:[EKEvent]) -> [EventItem] { //can this be variadic?
var calendarEvents = [EventItem]()
for event in events {
calendarEvents.append(eventItem(from: event))
}
return calendarEvents
}
static func eventTime(from date:Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .short
dateFormatter.locale = Locale.current
let stringTime = dateFormatter.string(from: date)
return stringTime
}
}
''
答案 0 :(得分:2)
我认为您在犯面向对象编程方面的一个基本错误。在您的Calendars类中,您似乎已经封装了用于访问用户日历的所有代码。然后,您似乎已经推理:“好吧,此代码需要可以在任何地方调用。因此,我班的所有成员都必须是全局的(静态/类)。”
那是一个错误。进行这种封装没有错;确实是一件好事。但是,使用封装的方法是使用辅助 instance 。例如,假设您在视图控制器中(毕竟很有可能)。然后可以有一个属性:
let calendarHelper = Calendars()
现在您的所有(或几乎所有)成员都可以(并且应该)成为 instance 成员。记住,相同类型的实例彼此之间分别保持状态。这是它们封装的一部分。您将需要这种能力。
如果您认为需要静态/类成员的根本原因是,在应用程序的生命周期内只需要一个 EKEventStore实例,则将全局性/静态性降低到该一个对象(例如,通过“共享的” EKEventStore及其访问方法),并使所有 else 成为普通实例成员。
答案 1 :(得分:1)
根据您的说法,怀疑1)是正确的-您需要使用singleton:
class CalendarService {
private var eventStore = EKEventStore()
//Static shared instance, this is your singleton
static var sharedInstance = CalendarService()
//Your public methods for adding events can go here
public func doSomething() {
//...
}
//As can your private methods for producing, deleting and editing calendar events + checking permissions
}
用法:
CalendarService.sharedInstance.doSomething()
没有您现有代码的特定示例,我真的不能说更多。