斯威夫特:太多的静态功能?

时间:2019-05-02 14:17:04

标签: swift macos

我有一个类,表示从事件存储中检索的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
    }


}

''

2 个答案:

答案 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()

没有您现有代码的特定示例,我真的不能说更多。