Xcode показывает переменную как необязательную, если это не так

У меня проблема с Xcode: переменная отображается как необязательная, хотя это не так.
Сначала я получаю ошибку в Xcode:

Значение необязательного типа «Необязательно» должно быть развернуто, чтобы ссылаться на элемент «timeFromDate» обернутого базового типа «Дата».

Поэтому я добавляю оператор !, а затем Xcode выдает следующую ошибку:

Невозможно принудительно развернуть значение необязательного типа «Дата».


Вот мой код:

import Foundation
import SwiftUI

struct DayView: View {
    
    @State var appointments: [Appointment] = []
    @State var dates = [
        Date(),
        Calendar.current.date(byAdding: .minute, value: 30, to: Date()),
        Calendar.current.date(byAdding: .hour, value: 60, to: Date()),
        Calendar.current.date(byAdding: .hour, value: 90, to: Date()),
        Calendar.current.date(byAdding: .hour, value: 120, to: Date())
    ]
    
    @State var selectedDate: Date?
    
    var date: Date
    
    var body: some View {
        ScrollView {
            VStack {
                Text(date.dayOfWeek())
                
                Text(date.fullMonthDayYearFormat())
                
                Divider().padding(.vertical)
                
                Text("Select a Time")
                    .font(.largeTitle)
                    .bold()
                
                Text("Duration: 30 Minutes")
                
                ForEach(dates, id: \.self) { date in
                    HStack {
                        Button {
                            withAnimation {
                                selectedDate = date
                                
                            }
                        } label: {
                            
                            Text(date!.timeFromDate())
                                .bold()
                                .padding()
                                .frame(maxWidth: .infinity)
                                .foregroundColor(selectedDate == date ? .white : .blue)
                                .background(
                                    ZStack {
                                        if selectedDate == date {
                                            RoundedRectangle(cornerRadius: 10)
                                                .foregroundColor(.gray)
                                        } else {
                                            RoundedRectangle(cornerRadius: 10).stroke()
                                        }
                                    }
                                )
                        }
                        if selectedDate == date {
                            NavigationLink {
                                BookingView(resident: Resident(firstName: "Andrew", middleName: "Warren Alexander", lastName: "Higbee", phoneNumber: "3852218786", address: "343 E 100 N Apt 12, Provo, UT, 84606", rentAmount: 800, pastDueRentOwed: 200, isPastDue: true, monthlyReminderScheduled: true, house: "Lion's Den", roomNumber: 3, bedNumber: 1, housePin: 7539, moveInDate: "2/9/2023"), date: date)
                            } label: {
                                Text("Next")
                                    .bold()
                                    .padding()
                                    .frame( maxWidth: .infinity)
                                    .foregroundColor(.white)
                                    .background(
                                        RoundedRectangle(cornerRadius: 10)
                                            .foregroundColor(.blue)
                                    )
                            }
                        }
                    }
                }
                .padding(.horizontal)
            }
        }
        .navigationTitle("Saturday")
        .navigationBarTitleDisplayMode(.inline)
        .onAppear(perform: {
            dates = timesWith30MinuteIncrements(for: date)
        })
    }
    
    func timesWith30MinuteIncrements(for date: Date) -> [Date] {
        let calendar = Calendar.current
        let startDate = calendar.date(bySettingHour: 8, minute: 0, second: 0, of: date)!
        let endDate = calendar.date(bySettingHour: 17, minute: 0, second: 0, of: date)!
        
        var times: [Date] = []
        var currentDate = startDate
        while currentDate <= endDate {
            times.append(currentDate)
            guard let newDate = calendar.date(byAdding: .minute, value: 30, to: currentDate) else { break }
            currentDate = newDate
        }
        
        return times
    }
}

Проблемная строка — Text(date.timeFromDate()).

Есть идеи?

Обновлено: я перестроил все приложение с нуля, скопировав и вставив. Я сократил код, чтобы изолировать проблему. В той же строке Text(date.timeFromDate()) дата заполняется как необязательная, и код строится таким образом.

Почему моя переменная необязательна?
Вот урезанный код:

import Foundation
import SwiftUI

struct DayView: View {
    @State var dates = [
        Date(),
        Calendar.current.date(byAdding: .minute, value: 30, to: Date()),
        Calendar.current.date(byAdding: .hour, value: 60, to: Date()),
        Calendar.current.date(byAdding: .hour, value: 90, to: Date()),
        Calendar.current.date(byAdding: .hour, value: 120, to: Date())
    ]
    
    @State var date: Date
    
    var body: some View {
        ScrollView {
            VStack {
                Text("August 29, 2023")
                
                Divider()
                    .padding(.vertical)
                
                Text("Select a Time")
                    .font(.largeTitle)
                    .bold()
                
                Text("Duration: 30 minutes")
                
                ForEach(dates, id: \.self) { date in
                    Button {
                        
                    } label: {
                        Text(date!.timeFromDate())
                    }
                }
            }
        }
        .navigationTitle("Saturday")
        .navigationBarTitleDisplayMode(.inline)
        .onAppear {
            dates = timesWith30MinuteIncrements(for: date)
        }
    }
    
    func timesWith30MinuteIncrements(for date: Date) -> [Date] {
        let calendar = Calendar.current
        let startDate = calendar.date(bySettingHour: 8, minute: 0, second: 0, of: date)!
        let endDate = calendar.date(bySettingHour: 18, minute: 0, second: 0, of: date)!
        
        var times: [Date] = []
        var currentDate = startDate
        while currentDate <= endDate {
            times.append(currentDate)
            guard let newDate = calendar.date(byAdding: .minute, value: 15, to: currentDate) else { break }
            currentDate = newDate
        }
        
        return times
    }
}


#Preview {
    NavigationStack {
        DayView(date: Date())
    }
}

Вот моя функция timeFromDate:

func timeFromDate() -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = "hh:mm a"
        return formatter.string(from: self)
    }

Я вижу ошибку. Значение типа «Дата» не имеет члена «dayOfWeek». Как реализованы расширения Date.dayOfWeek и Date.timeFromDate?

Benzy Neez 04.04.2024 20:58

Что такое timeFromDate()? Это тот, который возвращает необязательное значение? Вы имели в виду date.timeFromDate()! вместо date!.timeFromDate()?

Larme 04.04.2024 20:58

func timeFromDate() -> String { let formatter = DateFormatter() formatter.dateFormat = "чч:мм a" return formatter.string(from: self) }

AFinch 04.04.2024 20:59

Я испробовал все эти варианты с оператором взрыва. Он продолжает переключаться между ошибками, потому что это необязательно, и ошибкой, потому что это не является необязательным.

AFinch 04.04.2024 21:00

func dayOfWeek() -> String { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "EEEE" return dateFormatter.string(from: self) }

AFinch 04.04.2024 21:06

Покажите нам код, который действительно воспроизводит проблему. Сейчас у нас недостаточно информации.

matt 04.04.2024 21:43

Какую еще информацию я могу предоставить?

AFinch 05.04.2024 00:37
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
80
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Проблема, которую вы видите, связана не с @State var date, который не является обязательным, а с вводом даты в замыкание ForEach, т.е.

ForEach(dates, id: \.self) { date in  //<<< this one
                    Button {
                        
                    } label: {
                        Text(date!.timeFromDate())
                    }
                }

Это элемент вашего массива dates. Несколько элементов этого массива создаются с помощью метода Calendar.current.date(byAdding: value:), который возвращает необязательную дату, поэтому массив имеет подпись var dates: [Optional<Date>]. Вот почему компилятор говорит, что дату необходимо развернуть перед использованием.

Ответ принят как подходящий

если вы выберете вариант, нажмите .date(byAdding:, вы увидите, что он возвращает необязательную дату Date?. (нажатие на опцию dates также покажет, что она не является обязательной, и вы получите сообщение об ошибке, если скажете Swift, что она не должна быть необязательной dates: [Date])

Это означает, что тип вашего массива дат на самом деле является массивом необязательных дат [Date?]

Так что ваш date в ForEach(dates, id: \.self) { date in на самом деле необязателен.

Несколько способов справиться с этим в зависимости от ваших намерений,

Вы можете обернуть кнопку (или просто метку) if let date {

Вы можете сжать массив дат по умолчанию с помощью .compactMap { $0 }

Или вы можете выполнить логику где-то еще и передать массив необязательных значений в качестве начального значения при использовании DayView(dates = someNonOptionalDates).

Я думаю, что компилятор был чем-то сбит с толку, сообщив вам, что принудительное развертывание недоступно. Вероятно, это связано с вашим методом timeFromDate. (он не включен в пример кода, поэтому мне пришлось удалить его для тестирования). Вместо этого я использовал Text(date!, format: .dateTime), и это сработало нормально.

Завершенное примечание: очевидно, понятия не имею, что делает ваш метод, но, возможно, стоит посмотреть Text в таком формате, как if let date { Text(date, format: .dateTime.hour().minute()) }, если вы просто хотите показать время, он справится с большой сложностью и локализацией для вас!

редактировать -

Другой способ, поскольку вы уже защищаете от необязательной даты в другой логике даты, — это переместить ее в расширение. например. грубо:

extension Date {
    func thirtyMinuteIncrements(startHour: Int = 8, endHour: Int = 18) -> [Date] {
        ...
        let startDate = calendar.date(bySettingHour: startHour, minute: 0, second: 0, of: self)!
        ...
    }
}

Что может примерно сделать ваши onAppear и State

@State var dates = Date.now.thirtyMinuteIncrements()

.onAppear { dates = date.thirtyMinuteIncrements() }

с дополнительным преимуществом, что ваше состояние больше не является набором дополнительных опций, это просто [Date]

Другие вопросы по теме