Свифт: слишком много статических функций? - PullRequest
1 голос
/ 02 мая 2019

У меня есть класс, который представляет элементы календаря (модель), полученные из хранилища событий. Я еще не реализовал делегирование для AppDelegate или ViewControllers.

Все мои методы в этом классе являются статическими функциями - основная причина в том, что я могу "видеть" их из AppDelegate или VC. У меня есть подозрение, что:

1) Мне нужно сделать его одиночным - единственная функция которого - извлекать элементы календаря из хранилища событий и отправлять их в пользовательский интерфейс

2) научиться кодировать лучше - возможно, создав экземпляр класса в AppDelegate и VC

Это все еще очень нечетко для меня - не уверен, поможет ли публикация кода, но класс имеет кучу "static func .... doSomething () {...}" и вызывается AppDelegate и VC как "ClassName.doSomething () ..."

Я готов к рефакторингу кода класса, думая, что синглтон будет работать - или, может быть, все в порядке, как они ...

РЕДАКТИРОВАНИЕ: Добавление кода:

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 ]

2 голосов
/ 02 мая 2019

Я думаю, что вы делаете элементарную ошибку в объектно-ориентированном программировании.В вашем классе Calendars вы, кажется, инкапсулировали весь код для доступа к календарю пользователя.Тогда вы, кажется, рассуждаете: «Ну, этот код должен вызываться из любого места. Поэтому все члены моего класса должны быть глобальными (статические / классовые)».

Это ошибка.В этом нет ничего плохого;действительно это хорошо.Но тогда способ использовать ваша инкапсуляция - с помощью вспомогательного экземпляра .Например, предположим, что вы находитесь в контроллере представления (что наиболее вероятно в конце концов).Тогда он может иметь свойство:

let calendarHelper = Calendars()

Теперь все (или почти все) ваши участники могут (и должны) стать экземпляром участниками.Помните, что экземпляры одного и того же типа каждый получает возможность поддерживать состояние отдельно друг от друга;это часть их инкапсуляции.Вам понадобится эта способность.

Если ваша основная причина, по которой вы думаете, что вам нужны статические / ученики, состоит в том, что вам нужен только один EKEventStore экземпляр для жизни приложения, затем нажмитеглобальность / статичность вплоть до этого одного объекта (например, с помощью «общего» EKEventStore и методов доступа к нему), и пусть все else будет нормальным элементом экземпляра.

1 голос
/ 02 мая 2019

Из того, что вы сказали, подозрение 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()

Я не могу сказать намного больше без конкретных примеров вашего существующего кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...