struct CurrentDateView : View {
#State var now = Date()
let timer = Timer.publish(every: 1, on: .current, in: .common).autoconnect()
var body: some View {
Text("\(now)")
.onReceive(timer) {
self.now = Date()
}
}
}
This code is perfect, but I need the the Date format removed. I just need the HH:MM:SS showing
You can use a DateFormatter with Swift 5's string interpolation.
struct ContentView: View { #State var now = Date()
static let timeFormat: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "h:mm:ss a zz"
return formatter
}()
let timer = Timer.publish(every: 1, on: .current, in: .common).autoconnect()
var body: some View {
Text("\(now, formatter: Self.timeFormat)")
.onReceive(timer) {_ in
self.now = Date()
}
}
}
Related
I'm new to Swift and can't understand what's wrong with my code:
#State var selectedCourse: String = "1"
#State var napravlenie: [String] = [""]
var body: some View {
NavigationView {
ZStack{
VStack{
Text("Выберите вашу группу")
.padding(.bottom,80)
.font(.system(size: 30, weight: .semibold))
Text("Курс")
.font(.system(size: 28, weight: .bold))
.frame(width: 370,alignment: .leading)
//.padding()
Menu {
Picker(selection: $selectedCourse, label: EmptyView()) {
ForEach(course, id: \.self) {option in
Text (option)
.tag (1)
}
}
} label: {
customLabel_1
} .onChange(of: selectedCourse) { newValue in
napravlenie = viewModel.readValue(kyrs: newValue)
}
Text("Направление")
.font(.system(size: 28, weight: .bold))
.frame(width: 370,alignment: .leading)
.padding(.bottom,28)
Menu {
Picker(selection: $selectedNapravlenie, label: EmptyView()) {
ForEach(napravlenie, id: \.self) {option in
Text (option)
.tag (2)
}
}
} label: {
customLabel_2
}
}
}
the thing is that I want to update napravlenie array each time the first Picker is used and than pass that array as a ForEach of the second Picker, but it only updates after second trigger and shows an array that was triggered in the first time. I want the second Picker to update immediately, what should I do?
here's my viewModel code
import Foundation
import FirebaseCore
import FirebaseDatabase
class userViewModel: ObservableObject{
#Published var key = ""
#Published var moreKey = []
#Published var key2 = ""
#Published var moreKey2 = []
#Published var nameKey2 = ""
#Published var nameKey3 = ""
//#Published var nameMoreKey = []
#Published var object: String? = nil
#Published var userName: Any? = []
var ref = Database.database().reference()
// функция которая находит институты, из фб по выбору курса
func readValue(kyrs:String) -> [String]{
ref.child("Курс - " + kyrs).observeSingleEvent(of: .value) { snapshot in
for child in snapshot.children{ //тут он проверяет типо каждый раз нужный курс
let snap = child as! DataSnapshot
self.key = snap.key //все что тут находится я хз
self.moreKey.append(self.key)
print(self.moreKey)
}
}
let newArray: [String] = self.moreKey.compactMap { String(describing: $0) }
self.moreKey.removeAll()
return newArray
}
For example, when i choose anything in first Picker, the second one doesn't update napravlenie array, but after second trigger, the second picker shows array, that should've been presented after first trigger
I'm trying to show some section Header which is based on data in my structured array.
When I add a value in my array from my app I ask to enter a date. This one is save as a String cause I don't know how to save it differently from a Textfield..
So in my array I've got an enter "date" formatting like : DD/MM/YYYY
Now I need to have a view where I list all the array Items sorting by date, where the most recent date is show on the top of the screen and more the user scroll down more the date is far in a past.
So my structured array is defined like that :
struct Flight: Identifiable{
let id = UUID().uuidString
let date: String
let depPlace: String
let arrPlace: String
init (date: String, depPlace: String, arrPlace: String){
self.date = date
self.depPlace = depPlace
self.arrPlace = arrPlace
}
init(config: NewFlightConfig){
self.date = config.date
self.depPlace = config.depPlace
self.arrPlace = config.arrPlace
}
}
and NewFlightConfig :
struct NewFlightConfig{
var date: String = ""
var depPlace: String = ""
var arrPlace: String = ""
}
The TextField where I ask for the date :
TextField("DD/MM/YYYY", text: $flightConfig.date)
.padding()
.background(.white)
.cornerRadius(20.0)
.keyboardType(.decimalPad)
.onReceive(Just(flightConfig.date)) { inputValue in
if inputValue.count > 10 {
self.flightConfig.date.removeLast()
}else if inputValue.count == 2{
self.flightConfig.date.append("/")
}else if inputValue.count == 5{
self.flightConfig.date.append("/")
}
}
Finally my Homepage with my list which is defined as follow :
ScrollView{
VStack {
ForEach(flightLibrary.testFlight) {date in
Section(header: Text(date.date).font(.title2).fontWeight(.semibold)) {
ForEach(flightLibrary.testFlight) {flight in
ZStack {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(Color.white)
.shadow(color: Color(Color.RGBColorSpace.sRGB, white: 0, opacity: 0.2), radius: 4)
LogbookCellView(flight: flight)
}
}
}
}
}.padding(.horizontal, 16)
}
Where I've trying to deal with Dictionary to fill the SectionHeader Text but seems to didn't work...
var entryCollatedByDate: [String : [Flight]] {
Dictionary(grouping: flightLibrary, by: { $0.date })
}
I'm not very familiar with how to sorted data and grouped data into arrays.
My final objectif is to have something like that :
Section Header 1 -> 15/09/2022
Array Items 1 -> last items with same Header date
Array Items 2 -> last items with same Header date
Section Header 2 -> 14/09/2022
Array Items 3 -> last items with same Header date
Array Items 4 -> last items with same Header date
Array Items 5 -> last items with same Header date
[...]
Section Header N -> DD/MM/YYYY
Array Items N -> last items with same Header date
Hope to be clear about my problem
Thanks for your help
You could try this approach, where a function func asDate(...) is used to transform your String date
to a Date on the fly. Then using Set and map, to get unique dates for the sections.
These unique dates are sorted using the func asDate(...).
struct ContentView: View {
#State var flightLibrary = [Flight(date: "14/09/2022", depPlace: "depPlace-1", arrPlace: "arrPlace-1"),
Flight(date: "15/09/2022", depPlace: "depPlace-2", arrPlace: "arrPlace-2"),
Flight(date: "12/09/2022", depPlace: "depPlace-3", arrPlace: "arrPlace-3"),
Flight(date: "14/09/2022", depPlace: "depPlace-1.2", arrPlace: "arrPlace-1.2")]
func asDate(_ str: String) -> Date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
return dateFormatter.date(from: str) ?? Date()
}
#State var uniqueDates = [String]()
var body: some View {
ScrollView{
VStack {
ForEach(uniqueDates, id: \.self) { date in
Section(header: Text(date).font(.title2).fontWeight(.semibold)) {
ForEach(flightLibrary.filter({$0.date == date})) { flight in
ZStack {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(Color.white)
.shadow(color: Color(Color.RGBColorSpace.sRGB, white: 0, opacity: 0.2), radius: 4)
Text(flight.arrPlace)
}
}
}
}
}.padding(.horizontal, 16)
}
.onAppear {
// unique and sorted dates
uniqueDates = Array(Set(flightLibrary.map{$0.date})).sorted(by: {asDate($0) > asDate($1)})
}
}
}
An alternative approach is to change the String date in Flight to type Date.
Then using Set, map and sorted, to get unique and sorted dates for the sections.
struct ContentView: View {
#State var flightLibrary = [Flight]()
#State var uniqueDates = [Date]()
let frmt = DateFormatter()
var body: some View {
ScrollView{
VStack {
ForEach(uniqueDates, id: \.self) { date in
Section(header: Text(frmt.string(from: date)).font(.title2).fontWeight(.semibold)) {
ForEach(flightLibrary.filter({Calendar.current.isDate($0.date, inSameDayAs: date)})) { flight in
ZStack {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(Color.white)
.shadow(color: Color(Color.RGBColorSpace.sRGB, white: 0, opacity: 0.2), radius: 4)
Text(flight.arrPlace)
}
}
}
}
}.padding(.horizontal, 16)
}
.onAppear {
frmt.dateFormat = "dd/MM/yyyy"
frmt.timeZone = TimeZone(identifier: "UTC")
// for testing only
flightLibrary = [Flight(date: frmt.date(from: "14/09/2022")!, depPlace: "depPlace-1", arrPlace: "arrPlace-1"),
Flight(date: frmt.date(from: "15/09/2022")!, depPlace: "depPlace-2", arrPlace: "arrPlace-2"),
Flight(date: frmt.date(from: "12/09/2022")!, depPlace: "depPlace-3", arrPlace: "arrPlace-3"),
Flight(date: frmt.date(from: "14/09/2022")!, depPlace: "depPlace-1.2", arrPlace: "arrPlace-1.2")]
// unique and sorted dates
uniqueDates = Array(Set(flightLibrary.map{$0.date})).sorted(by: {$0 > $1})
}
}
}
struct Flight: Identifiable, Hashable {
let id = UUID().uuidString
let date: Date // <-- here
let depPlace: String
let arrPlace: String
init (date: Date, depPlace: String, arrPlace: String){ // <-- here
self.date = date
self.depPlace = depPlace
self.arrPlace = arrPlace
}
init(config: NewFlightConfig) {
self.date = config.date
self.depPlace = config.depPlace
self.arrPlace = config.arrPlace
}
}
struct NewFlightConfig {
var date: Date = Date() // <-- here
var depPlace: String = ""
var arrPlace: String = ""
}
EDIT-1: here is another approach that uses a class FlightModel: ObservableObject
to hold your data and update the UI whenever flights is changed. It also has a convenience computed property for theuniqueDates.
So in your addView, pass the flightModel to it (e.g #EnvironmentObject) and add new Flight to the flightModel.
class FlightModel: ObservableObject {
#Published var flights = [Flight]()
var uniqueDates: [Date] {
let arr = flights.compactMap{frmt.date(from: frmt.string(from: $0.date))}
return Array(Set(arr.map{$0})).sorted(by: {$0 > $1})
}
let frmt = DateFormatter()
init() {
frmt.dateFormat = "dd/MM/yyyy"
frmt.timeZone = TimeZone(identifier: "UTC")
getData()
}
func getData() {
// for testing only
flights = [
Flight(date: Date(), depPlace: "LFLI", arrPlace: "LFLP"),
Flight(date: Date(), depPlace: "LFLP", arrPlace: "LFLB"),
Flight(date: Date(), depPlace: "LFLB", arrPlace: "LFLG"),
Flight(date: frmt.date(from: "14/09/2022")!, depPlace: "depPlace-1", arrPlace: "arrPlace-1"),
Flight(date: frmt.date(from: "15/09/2022")!, depPlace: "depPlace-2", arrPlace: "arrPlace-2"),
Flight(date: frmt.date(from: "12/09/2022")!, depPlace: "depPlace-3", arrPlace: "arrPlace-3"),
Flight(date: frmt.date(from: "14/09/2022")!, depPlace: "depPlace-1.2", arrPlace: "arrPlace-1.2")
]
}
}
struct ContentView: View {
#StateObject var flightModel = FlightModel()
var body: some View {
ScrollView {
VStack {
ForEach(flightModel.uniqueDates, id: \.self) { date in
Section(header: Text(flightModel.frmt.string(from: date)).font(.title2).fontWeight(.semibold)) {
ForEach(flightModel.flights.filter({Calendar.current.isDate($0.date, inSameDayAs: date)})) { flight in
ZStack {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(Color.white)
.shadow(color: Color(Color.RGBColorSpace.sRGB, white: 0, opacity: 0.2), radius: 4)
Text(flight.arrPlace)
}
}
}
}
}.padding(.horizontal, 16)
}
}
}
In response to #workingdog support Ukraine in the comments just before.
I have no errors in my code but with this array :
class FlightLibrary: ObservableObject{
#Published var testFlight = [
Flight(date: Date(), depPlace: "LFLI", arrPlace: "LFLP"),
Flight(date: Date(), depPlace: "LFLP", arrPlace: "LFLB"),
Flight(date: Date(), depPlace: "LFLB", arrPlace: "LFLG")
]
}
This return something like :
Section Header 1 : Today Date
Item 1 :testFlight[0]
Item 2 :testFlight[0]
Item 3 :testFlight[0]
Section Header 2 : Today Date
Item 1 :testFlight[0]
Item 2 :testFlight[0]
Item 3 :testFlight[0]
When I append a value into testFlight, via a page in the app where user could fill some textFields to set date, dePlace and arrPlace, then dismiss my page the scrollview is not updated correctly. I've my new item but I have no Section Header ...
My add button code in the other view :
#Environment(\.dismiss) private var dismiss
#ObservedObject var flightLibrary: FlightLibrary
#State var flightConfig = NewFlightConfig()
.navigationBarItems(trailing: Button(action: {
let newFlight = Flight(config: flightConfig)
flightLibrary.testFlight.append(newFlight)
dismiss()
}, label: {
Text("Save")
}))
To update the scrollview I go in an other page and back (to reload the .onAppear) to the page where I have my scrollview and now it works with all section header but !
If there is only one date section header is correct but if there is two or more item with the same date it create a new section header for each item but it add all the item with the same date in each section ...
Exemple :
Item1 = Date 1
Item2 = Date 1
Item3 = Date 2
Item4 = Date 1
result of :
Section Header 1 : Date1
Item 1
Item 2
Item 4
Section Header 2 : Date1
Item 1
Item 2
Item 4
Section Header 3 : Date1
Item 1
Item 2
Item 4
Section Header 4 : Date2
Item 3
After long time developing in Swift with storyboard I decide to move to Swift UI.
I make a small project to do that. This project is to simply read a CSV file and show some informations in a List and make this list searchable.
I've already been able to read the CSV file and then open some informations in a List. I've understand approximately how the protocol work but I'm definitely looked with the searching function ...
Here is my "mainView" code :
struct AirportView: View {
#State private var searchText = ""
#ObservedObject var airportsVM:AirportsViewModel = AirportsViewModel()
var body: some View {
let airportsDB = airportsVM.ListAirports
NavigationView {
List{
Section(header: Text("Suisse")){
Text("Hello World")
}
Section(header: Text("France")) {
ForEach(airportsDB, id:\.self) { listAirport in
NavigationLink(destination: Text(listAirport.ApType)) {
Text("\(listAirport.Icao) - \(listAirport.Name)")
}
}
}
}
.id(UUID())
}
.navigationTitle("Airports")
.searchable(text: $searchText)
.navigationViewStyle(StackNavigationViewStyle())
}
and here is my "CSV Reader" Code
import Foundation
struct Airports2: Identifiable, Codable, Hashable{
var AirportID: String = ""
var Icao: String = ""
var ApType: String = ""
var Name: String = ""
var Latitude: String = ""
var Longitude: String = ""
var Altitude: String = ""
var Continent: String = ""
var Country: String = ""
var Region: String = ""
var Municipality: String = ""
var Service: String = ""
var GpsCode: String = ""
var Iata: String = ""
var LocalCode: String = ""
var Link: String = ""
var wikiLink: String = ""
var Keyword: String = ""
let id = UUID()
}
var airportsDB = [Airports2]()
class AirportsClass {
static let
bundleURL = Bundle.main.url(forResource: "FR_Airports", withExtension: "csv")!
static func retrieveAP() -> [Airports2] {
guard let data = try? Data(contentsOf: bundleURL) else {
fatalError("Unable to load airports data")
}
let decoder = String(data: data, encoding: .utf8)
if let dataArr = decoder?.components(separatedBy: "\n").map({ $0.components(separatedBy: ",") })
{
var i = 0
for line in dataArr
{
i += 1
if i < dataArr.count {
let item = Airports2(AirportID: line[0], Icao: line[1], ApType: line[2], Name: line[3], Latitude: line[4], Longitude: line[5], Altitude: line[6], Continent: line[7], Country: line[8], Region: line[9], Municipality: line[10], Service: line[11], GpsCode: line[12], Iata: line[13], LocalCode: line[14], Link: line[15], wikiLink: line[16], Keyword: line[17])
airportsDB.append(item)
}
}
}
return airportsDB
}
}
class AirportsViewModel: NSObject, ObservableObject {
#Published var ListAirports = [Airports2]()
override init() {
ListAirports = AirportsClass.retrieveAP()
}
}
Thanks for your help !
struct AirportView: View {
#State private var searchText = ""
private let airportsVM = AirportsClass.retrieveAP()
var filteredResults: [Airports2] {
if searchText.isEmpty {
return airportsVM.ListAirports
} else {
// some sort of filtering - you'll probably need to filter on multiple properties
return airportsVM.ListAirpots.filter { $0.Name.contains(searchText) }
}
}
var body: some View {
NavigationView {
List{
Section(header: Text("Suisse")){
Text("Hello World")
}
Section(header: Text("France")) {
ForEach(filteredResults, id:\.self) { airport in
NavigationLink(destination: Text(airport.ApType)) {
Text("\(airport.Icao) - \(airport.Name)")
}
}
}
}
.id(UUID())
}
.navigationTitle("Airports")
.searchable(text: $searchText)
.navigationViewStyle(StackNavigationViewStyle())
}
}
This is my data structure
struct SPPWorkout: Codable {
static let setKey = "Sets"
static let exerciseID = "id"
var id: Double? = 0.0
var duration: String?
var calories: Int?
var date: String?
var exercises: [ExerciseSet]
[...]
}
struct ExerciseSet: Codable {
let id: String
let name: String
var reps: Int
var weight: Double
[...]
}
extension ExerciseSet: Equatable {
static func ==(lhs: ExerciseSet, rhs: ExerciseSet) -> Bool {
return lhs.id == rhs.id
}
}
and in a SwiftUI view I'm trying to modify an ExerciseSet from user input
#State private var sppWorkout: SPPWorkout!
EditSetPopup(isShowingOverlay: $isShowingOverlay,
update: { reps, weight in
guard let editingIndex = editingIndex else { return }
sppWorkout.exercises[editingIndex].reps = Int(reps) ?? 0
sppWorkout.exercises[editingIndex].weight = Double(weight) ?? 0.0
self.editingIndex = nil
})
}
The issue is here
sppWorkout.exercises[editingIndex].reps = Int(reps) ?? 0
sppWorkout.exercises[editingIndex].weight = Double(weight) ??
and I've tried in all ways to update it, both from the view and with a func in SPPWorkout. I've also tried to replace the object at index
var newSet = ExerciseSet(id: [...], newValues)
self.exercises[editingIndex] = newSet
but in no way it wants to update. I'm sure that somewhere it creates a copy that it edits but I have no idea why and how to set the new values.
Edit: if I try to delete something, it's fine
sppWorkout.exercises.removeAll(where: { $0 == sppWorkout.exercises[index]})
Edit 2:
It passes the guard statement and it does not change the values in the array.
Edit 3:
At the suggestion below from Jared, I've copied the existing array into a new one, set the new values then tried to assign the new one over to the original one but still, it does not overwrite.
EditSetPopup(isShowingOverlay: $isShowingOverlay,
update: { reps, weight in
print(sppWorkout.exercises)
guard let editingIndex = editingIndex else { return }
var copyOfTheArray = sppWorkout.exercises
copyOfTheArray[editingIndex].reps = Int(reps) ?? 0
copyOfTheArray[editingIndex].weight = Double(weight) ?? 0.0
//Copy of the array is updated correctly, it has the new values
sppWorkout.exercises = copyOfTheArray
//Original array doesn't get overwritten. It still has old values
self.editingIndex = nil
Edit 4: I've managed to make progress by extracting the model into a view model and updating the values there. Now the values get updated in sppWorkout, but even though I call objectWillChange.send(), the UI Update doesn't trigger.
full code:
class WorkoutDetailsViewModel: ObservableObject {
var workoutID: String!
#Published var sppWorkout: SPPWorkout!
func setupData(with workoutID: String) {
sppWorkout = FileIOManager.readWorkout(with: workoutID)
}
func update(_ index: Int, newReps: Int, newWeight: Double) {
let oldOne = sppWorkout.exercises[index]
let update = ExerciseSet(id: oldOne.id, name: oldOne.name, reps: newReps, weight: newWeight)
sppWorkout.exercises[index] = update
self.objectWillChange.send()
}
}
struct WorkoutDetailsView: View {
var workoutID: String!
#StateObject private var viewModel = WorkoutDetailsViewModel()
var workout: HKWorkout
var dateFormatter: DateFormatter
#State private var offset = 0
#State private var isShowingOverlay = false
#State private var editingIndex: Int?
#EnvironmentObject var settingsManager: SettingsManager
#Environment(\.dismiss) private var dismiss
var body: some View {
if viewModel.sppWorkout != nil {
VStack {
ListWorkoutItem(workout: workout, dateFormatter: dateFormatter)
.padding([.leading, .trailing], 10.0)
List(viewModel.sppWorkout.exercises, id: \.id) { exercise in
let index = viewModel.sppWorkout.exercises.firstIndex(of: exercise) ?? 0
DetailListSetItem(exerciseSet: viewModel.sppWorkout.exercises[index], set: index + 1)
.environmentObject(settingsManager)
.swipeActions {
Button(role: .destructive, action: {
viewModel.sppWorkout.exercises.removeAll(where: { $0 == viewModel.sppWorkout.exercises[index]})
} ) {
Label("Delete", systemImage: "trash")
}
Button(role: .none, action: {
isShowingOverlay = true
editingIndex = index
} ) {
Label("Edit", systemImage: "pencil")
}.tint(.blue)
}
}
.padding([.leading, .trailing], -30)
//iOS 16 .scrollContentBackground(.hidden)
}
.overlay(alignment: .bottom, content: {
editOverlay
.animation(.easeInOut (duration: 0.5), value: isShowingOverlay)
})
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: Button(action : {
do {
try FileIOManager.write(viewModel.sppWorkout, toDocumentNamed: "\(viewModel.sppWorkout.id ?? 0).json")
} catch {
Debugger.log(error: error.localizedDescription)
}
dismiss()
}){
Image(systemName: "arrow.left")
})
} else {
Text("No workout details found")
.italic()
.fontWeight(.bold)
.font(.system(size: 35))
.onAppear(perform: {
viewModel.setupData(with: workoutID)
})
}
}
#ViewBuilder private var editOverlay: some View {
if isShowingOverlay {
ZStack {
Button {
isShowingOverlay = false
} label: {
Color.clear
}
.edgesIgnoringSafeArea(.all)
VStack{
Spacer()
EditSetPopup(isShowingOverlay: $isShowingOverlay,
update: { reps, weight in
guard let editingIndex = editingIndex else { return }
print(viewModel.sppWorkout.exercises)
print("dupa aia:\n")
viewModel.update(editingIndex, newReps: Int(reps) ?? 0, newWeight: Double(weight) ?? 0.0)
print(viewModel.sppWorkout.exercises)
self.editingIndex = nil
})
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color("popupBackground"),
lineWidth: 3)
)
}
}
}
}
}
So I got a very good explanation on reddit on what causes the problem. Thank you u/neddy-seagoon if you are reading this.
The explanation
. I believe that updating an array will not trigger a state update. The only thing that will, with an array, is if the count changes. So
sppWorkout.exercises[index].reps = newReps
will not cause a trigger. This is not changing viewModel.sppWorkout.exercises.indices
So all I had to to was modify my List from
List(viewModel.sppWorkout.exercises, id: \.id)
to
List(viewModel.sppWorkout.exercises, id: \.hashValue)
as this triggers the list update because the hashValue does change when updating the properties of the entries in the list.
For the line
List(viewModel.sppWorkout.exercises, id: \.id) { exercise in
Replace with
List(viewModel.sppWorkout.exercises, id: \.self) { exercise in
I have an integer array which is a collection of today's and tomorrow's dates, I want to separate the integer array based on the type of day
let dateCollection = [
1633722900,
1633730500,
1633754910,
1633758913,
1633820400,
1633824000,
1633827600,
1633831200,
1633834800,
1633838400,
1633842000
]
expected result
let today: [Int] = [
1633722900,
1633730500,
1633754910,
1633758913
]
let tomorrow: [Int] = [
1633820400,
1633824000,
1633827600,
1633831200,
1633834800,
1633838400,
1633842000
]
what should i do to separate them, i have made an extension to convert the integer to date or vice versa, and display it as a time, i already create the date to time extension too
func getTimetringFromINT() -> String {
let date = Date(timeIntervalSince1970: TimeInterval(self))
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "id")
dateFormatter.dateFormat = "HH:mm"
return dateFormatter.string(from: date)
}
selector i made, after convert the date to Time string
You can use Calendar.current.ordinality to compare the day of the year for the various dates. Here's an example that generates dates from today and tomorrow and then filters them back into separate arrays:
let today = Date()
let todayInts = Array(0..<10).map { today.timeIntervalSince1970 + TimeInterval($0) }
print("Today:", todayInts,"\n")
let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: today)!
let tomorrowInts = Array(0..<10).map { tomorrow.timeIntervalSince1970 + TimeInterval($0) }
print("Tomorrow:", tomorrowInts,"\n")
let allInts = todayInts + tomorrowInts
let todayDayOfYear = Calendar.current.ordinality(of: .day, in: .year, for: today)!
let filteredTodayInts = allInts.filter { Calendar.current.ordinality(of: .day, in: .year, for: Date(timeIntervalSince1970: $0)) == todayDayOfYear }
print("Filtered today:", filteredTodayInts,"\n")
let tomorrowDayOfYear = todayDayOfYear + 1
let filteredTomorrowInts = allInts.filter { Calendar.current.ordinality(of: .day, in: .year, for: Date(timeIntervalSince1970: $0)) == tomorrowDayOfYear }
print("Filtered tomorrow:", filteredTomorrowInts,"\n")
An efficient way to do this would be to calculate the int value of midnight between today and tomorrow and then split the array based on that value
let calendar = Calendar.current
var today = [Int]()
var tomorrow = [Int]()
if let midnight = calendar.date(byAdding: .day, value: 1, to: calendar.startOfDay(for: .now))?.timeIntervalSince1970 {
let limit = Int(midnight)
dateCollection.forEach { $0 < limit ? today.append($0) : tomorrow.append($0) }
}
Another approach is to get the start date of the next day (regardless of daylight saving time changes) with the Calendar API and then partition the array
let startOfTomorrow = Calendar.current.nextDate(after: Date(),
matching: DateComponents(hour: 0),
matchingPolicy: .nextTime)!
.timeIntervalSince1970
let splitIndex = dateCollection.partition{ $0 < Int(startOfTomorrow) }
let tomorrowDates = dateCollection[..<splitIndex]
let todayDates = dateCollection[splitIndex...]
To be able to run this declare dateCollection as var
By Getting StartOfDay of any Time, we can classify all Date that shares the same StartOfDay.
extension Date {
var morning: Date { Calendar.current.startOfDay(for: self) }
var nextMorning: Date { Calendar.current.date(byAdding: .day, value: 1, to: self)!.morning }
}
extension Int {
var since1970: Date { Date(timeIntervalSince1970: TimeInterval(self)) }
}
In this case:
let todayMorning = Date().morning // Date().advanced(by: 3600*24 * -19).morning
let tomorrowMorning = Date().nextMorning // Date().advanced(by: 3600*24 * -19).nextMorning
let dateCollection = [ ... ]
let today = dateCollection.filter{ $0.since1970.morning == todayMorning }
let tomorrow = dateCollection.filter{ $0.since1970.morning == tomorrowMorning }