Swift 斯威夫特:静态函数太多了?
我有一个表示从事件存储中检索到的日历项(模型)的类。我还没有为AppDelegate或ViewController实现任何委托 我在这个类中的所有方法都是静态函数——主要原因是我可以从AppDelegate或VC中“看到”它们。我怀疑: 1) 我需要将其设置为singleton,它唯一的功能是从eventStore检索日历项并发布到UI 2) 了解如何更好地编写代码-可能在AppDelegate和VC中创建类的实例 这对我来说仍然很模糊-不确定发布代码是否有帮助,但该类有一堆“static func….doSomething(){…}”,并且被AppDelegate和VC称为“ClassName.doSomething()…” 我准备重构类代码,认为一个单例可以工作——或者可能一切都很好 编辑:添加代码:Swift 斯威夫特:静态函数太多了?,swift,macos,Swift,Macos,我有一个表示从事件存储中检索到的日历项(模型)的类。我还没有为AppDelegate或ViewController实现任何委托 我在这个类中的所有方法都是静态函数——主要原因是我可以从AppDelegate或VC中“看到”它们。我怀疑: 1) 我需要将其设置为singleton,它唯一的功能是从eventStore检索日历项并发布到UI 2) 了解如何更好地编写代码-可能在AppDelegate和VC中创建类的实例 这对我来说仍然很模糊-不确定发布代码是否有帮助,但该类有一堆“static fu
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
}
}
''从你所说的,怀疑1)是正确的-你需要使用: 用法:
CalendarService.sharedInstance.doSomething()
如果没有您现有代码的具体示例,我真的说不出更多。我认为您在面向对象编程方面犯了一个基本错误。在Calendars类中,您似乎已经封装了访问用户日历的所有代码。然后,您似乎得出了这样的结论:“这个代码需要可以从任何地方调用。因此,我的类的所有成员都需要是全局的(静态/类)。” 那是个错误。做这样的封装没有错;这的确是件好事。但是,使用封装的方法是使用helper实例。例如,假设您在一个视图控制器中(这很有可能)。然后它可以有一个属性:
let calendarHelper = Calendars()
现在,您的所有(或几乎所有)成员都可以(而且应该)成为实例成员。请记住,同一类型的实例各自独立地维护状态;这是它们封装的一部分。你会想要那种能力的
如果您认为需要静态/类成员的根本原因是在应用程序的生命周期中只需要一个EKEventStore实例,则将全局性/静态性向下推到该对象上(例如,通过“共享”EKEventStore和访问它的方法)让所有其他内容都成为普通的实例成员。也许可以转到我猜你会因为“太宽泛”或“基于意见”而在这里被否决@Gereon:代码审查需要从具体项目中获得工作代码。以目前的形式,这个问题在CR上是离题的(缺少上下文)。可能有帮助:。感谢您的输入-在原始PostOk中添加了代码,因此您需要一个
静态var sharedInstance=Calendars()
(如上所述)来确保您有一个单身。然后,您可以遍历并删除静态
/类
前缀,例如,通过Calendars.sharedInstance.yourPublicMethod()
将您需要公开的内容公开为公共
。我建议将Calendars
重命名为CalendarService
,以明确这是处理创建/删除/编辑日历事件的唯一方法。为此,我还阅读了您提供的链接,它们也很好阅读。有关此模式的典型示例,请参阅我的。这里我们有一个类ManagerHolder,它对CLLocationManager的作用与您的类对EKEventStore的作用相同。看看视图控制器是如何合并这个类的一个实例的。matt-谢谢你-这是非常有洞察力的-我也可以看到这个主题如何进入基于灰色观点的领域-但是你的方法在这里似乎是明智的-我想很多新手程序员(我自己)他们面临着授权、依赖注入等学习模式的“艰巨”任务——这就是为什么我问我的问题:我想解决这些问题,因为这是真正学习的唯一途径。
let calendarHelper = Calendars()