SwiftUI - Firebase - Save Date without time - ios

hi everyone I have this structure that I use to save data on firebase ... As you can see I have a var reservation_date: Date field
Firebase obviously saves a date with the time but I would need only the date without the time to be saved how can I get this?
This is how I save me
struct Reservations: Identifiable, Codable {
#DocumentID var id: String?
var reservation_date: Date
var user: String
#ExplicitNull var userID: String?
var reservation_id: String
#FirebaseFirestoreSwift.ServerTimestamp var createdAt: Timestamp?
enum CodingKeys: String, CodingKey {
case id
case reservation_date
case user
case userID
case reservation_id
case createdAt
}
static let sample = Reservations(reservation_date: Date(), user: "", userID: "", reservation_id: "")
}
// MARK: - Firebase
extension ReservationViewModel {
func addReservation() {
guard let newDate = Calendar.current.date(bySettingHour: hour, minute: minute, second: 0, of: selectedDate) else {
print("Non è stato possibile creare una data di prenotazione valida")
return }
let dBPath = db.collection("Prenotazioni")
.document(newDate.formatted(.dateTime.year()))
.collection(newDate.formatted(.dateTime.month(.wide).locale(Locale(identifier: "it"))))
.document(newDate.formatted(.dateTime.day(.twoDigits)))
.collection("Ore \(newDate.formatted(.dateTime.hour(.twoDigits(amPM: .omitted)).locale(Locale(identifier: "it"))) + ":" + newDate.formatted(.dateTime.minute(.twoDigits)))")
do {
let _ = try dBPath.addDocument(from: reservation)
}
catch {
print(error)
}
}
}

If you're only interested in the date value then I would recommend using a string that conforms to the ISO 8601 standard, which in this particular case would just be yyyy-mm-dd (ex: "2022-02-13"). Swift has a ready-made ISO-8601 formatter exactly for this.
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withFullDate]
// From String to Date
let reservationDay = "2022-03-25" // save this in your database
if let date = formatter.date(from: reservationDay) {
print(date) // Swift Date object
}
// From Date to String
let reservationDate = Date()
let day = formatter.string(from: reservationDate)
print(day) // String "2022-02-13"
Be mindful of timezones, however, because the default is GMT and you will want to use GMT throughout the conversions (and then render the local timezone value) or the local timezone throughout the conversions (just don't mix them as you convert). To always deal in the local timezone you can set its property on the formatter:
formatter.timeZone = .current

Related

To receive by Swift DateFormatter utc?

I would like to receive the date value in the api value as utc. I looked up the stackoverflow.
There was a similar case, but we couldn't solve it because it was different from me.
The server (POSTMAN(db) stores the value "b_date": 1602813891.
link >> Dateformatter issue in swift
mycode
var ViewItem: BoardView?
func DetailViewList() {
DispatchQueue.main.async {
self.txtUserName.text = String(ViewItem.userId ?? "")
self.txtCount.text = String(ViewItem.b_count ?? 0)
}
}
func utcToLocal(utcDate: String, dateFormat: String) -> String {
let dfFormat = DateFormatter()
dfFormat.dateFormat = dateFormat
dfFormat.timeZone = TimeZone(abbreviation: "UTC")
let dtUtcDate = dfFormat.date(from: utcDate)
dfFormat.timeZone = TimeZone.current
dfFormat.dateFormat = dateFormat
txtDate.text = Int(ViewItem?.b_date) // ERROR [Cannot invoke initializer for type 'Int' with an argument list of type '(Int?)'] , Overloads for 'Int' exist with these partially matching parameter lists: (CGFloat), (Double), (Float), (Float80), (Int64), (Word), (__shared NSNumber)
return dfFormat.string(from: dtUtcDate!)
}
jsonData
struct BoardView: Codable {
var b_date: Int?
var b_count: Int?
var userId: String?
}

Save array of Days in Swift 5

So i have created a class for a Day and for a Drink. and I'm trying to track how much you drink in a day, but I'm struggling with saving multiple days. I'm currently managing to save the current day(with the amount drunk that day) but i don't know how to save more than one day.
I want to save an array of type Day with all the days. how can i do this?
This is my Day class:
public class Day: NSObject {
var date: Date
var goalAmount: Drink
var consumedAmount: Drink
func saveDay() {
let formatting = DateFormatter()
formatting.dateFormat = "EEEE - dd/mm/yy"
UserDefaults.standard.set(formatting.string(from: date), forKey: "date")
UserDefaults.standard.set(goalAmount.amountOfDrink, forKey: "goal")
UserDefaults.standard.set(consumedAmount.amountOfDrink, forKey: "consumed")
}
func loadDay() {
let rawDate = UserDefaults.standard.value(forKey: "date") as? String ?? ""
let formatter = DateFormatter()
formatter.dateFormat = "EEEE - dd/mm/yy"
date = formatter.date(from: rawDate)!
goalAmount.amountOfDrink = UserDefaults.standard.float(forKey: "goal")
consumedAmount.amountOfDrink = UserDefaults.standard.float(forKey: "consumed")
}
}
This is my Drink class:
class Drink: NSObject {
var typeOfDrink: String
var amountOfDrink: Float
}
i am calling saveDay() when there are any changes made to the day, and then loadDay() when the app opens.
A better approach would be is to store the object of the class in userDefaults instead of storing particular properties of that class. And use [Date] instead of Date to save multiple days
For this first, you have Serialize the object to store in userDefaults and Deserialize to fetch the data from userDefaults.
import Foundation
class Day: Codable {
var date = Date()
var goalAmount: Drink
var consumedAmount: Drink
init(date: Date, goalAmount: Drink,consumedAmount: Drink ) {
self.date = date
self.goalAmount = goalAmount
self.consumedAmount = consumedAmount
}
static func saveDay(_ day : [Day]) {
do {
let object = try JSONEncoder().encode(day)
UserDefaults.standard.set(object, forKey: "days")
} catch {
print(error)
}
}
static func loadDay() {
let decoder = JSONDecoder()
if let object = UserDefaults.standard.value(forKey: "days") as? Data {
do {
let days = try decoder.decode([Day].self, from: object)
for day in days {
print("Date - ", day.date)
print("Goal Amount - ", day.goalAmount)
print("Consumed Amount - ",day.consumedAmount)
print("----------------------------------------------")
}
} catch {
print(error)
}
} else {
print("unable to fetch the data from day key in user defaults")
}
}
}
class Drink: Codable {
var typeOfDrink: String
var amountOfDrink: Float
init(typeOfDrink: String,amountOfDrink: Float ) {
self.typeOfDrink = typeOfDrink
self.amountOfDrink = amountOfDrink
}
}
Use saveAndGet() method to store and fetch details from userDefaults
func saveAndGet() {
// use any formats to format the dates
let date = Date()
let goalAmount = Drink(typeOfDrink: "Water", amountOfDrink: 5.0)
let consumedAmount = Drink(typeOfDrink: "Water", amountOfDrink: 3.0)
let day1 = Day(date: date, goalAmount: goalAmount, consumedAmount: consumedAmount)
let day2 = Day(date: date, goalAmount: goalAmount, consumedAmount: consumedAmount)
let day3 = Day(date: date, goalAmount: goalAmount, consumedAmount: consumedAmount)
let day4 = Day(date: date, goalAmount: goalAmount, consumedAmount: consumedAmount)
let days = [day1, day2, day3, day4]
Day.saveDay(days)
Day.loadDay()
}
1) You need to create array of object for this :
goalAmount = [Drink]()
var date = [Date]()
and append with each new element.
you can also add date variable inside your drink class.
2) you can also create array of dictionary:
var userData = [String : Any]()
key will be you date and Any contain related to drink data in Any you can store Anything.

(Swift) timeIntervalSince1970 stored in Realm with current dateTime, but showing wrong date when it is read?

func getCurrentTimeIntervalSince1970()-> Int
{
return Int(NSDate().timeIntervalSince1970)
}
let lastLearned = getCurrentTimeIntervalSince1970()
let cardData = "\(cardId):\(newCardLevel):\(lastLearned)"
Note: Everything except lastLearned is unimportant. lastLearned is where I store current time in TimeIntervalSince1970.
let array = cardData.components(separatedBy: ":")
print("lastlearned : \(array[2])") //result: `2020-02-26 10:28:38.467046+0100`
Storing lastLearned in Realm:
RealmManager.shared.updateLevel(lastLearned: array[2])
Printing date from Realm:
let date = Date(timeIntervalSince1970: Double(card.last_learned)!)
print(date) //result: 2106-02-07 06:28:15 +0000 //HOW?
Update your getCurrentTimeIntervalSince1970 func to this
func getCurrentTimeIntervalSince1970() -> Int {
return Int(Date().timeIntervalSince1970)
}
let date = getCurrentTimeIntervalSince1970()
debugPrint(date)
debugPrint(Date(timeIntervalSince1970: Double(date)))
Result
1582711543
2020-02-26 10:05:43 +0000

iOS swift Codable not working with Alamofire for JSON nested data?

I am new to Alamofire and Codable concept In iOS can someone tell me how to use this to access my json data.
This is my json response.
{"subscriptions": [
{
"batch_user_id": 23,
"batch_name": "demo batch",
"course_name": "IELTS",
"start_date": "Nov 01 2019",
"end_date": "Nov 30 2019",
"no_of_days": 21,
"total_no_of_days": 30,
"extended_date": "Nov 30 2019",
"extended": false,
"course_extensions": [
{
"id": 31,
"amount": "3500.0",
"course_id": 1,
"is_active": true,
"number_of_days": 5
},
this is codable code:
struct course_extensions: Codable {
let id: String
let amount: String
let course_id: String
private enum CodingKeys: String, CodingKey {
case id = "id"
case amount = "amount"
case course_id = "course_id"
}
}
struct subscriptions: Codable {
let batch_user_id: String
let batch_name: String
let course_extensions: course_extensions
private enum CodingKeys: String, CodingKey {
case batch_user_id
case batch_name
case course_extensions = "course_extensions"
}
}
struct User: Codable {
let status: String
let message: String
let subscriptions: subscriptions
}
This is my service call with alamofire:
// MARK: - Service call
func fetchUserData() {
AF.request(SMAConstants.my_subscriptions, method: .get, parameters: nil, headers: nil)
.responseJSON { (response) in
switch response.result {
case .success(let value):
let swiftyJsonVar = JSON(value)
print(swiftyJsonVar)
case .failure(let error):
print(error)
}
}
}
Can some one help me out access nested array data with codable . thanks in advance.
You’re missing the structure for that outermost portion of the JSON:
struct ResponseObject: Codable {
let subscriptions: [Subscription]
}
And, you can use normal camelCase properties:
struct Subscription: Codable {
let batchUserId: Int
let batchName: String
let courseExtensions: [CourseExtension]
}
struct CourseExtension: Codable {
let id: Int
let amount: String
let courseId: Int
let isActive: Bool
}
A few observations:
The struct type names should begin with uppercase letters as a matter of convention.
Those CodingKeys are not necessary in this case.
Be careful on your types. A number of these are Int and Bool. Only use String types if the value is in quotation marks.
Obviously, I’ve excluded a few properties from the above struct types for the sake of brevity, but add any missing properties, but sticking with the camelCase convention.
Anyway, you can then tell the decoder to convert snake_case JSON keys to camelCase property names using:
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let object = try decoder.decode(ResponseObject.self, from: data)
print(object.subscriptions)
} catch {
print(error)
}
So, for example, if using Alamofire 5:
let decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return decoder
}()
func fetchUserData() {
AF.request(SMAConstants.mySubscriptions))
.responseDecodable(of: ResponseObject.self, decoder: decoder) { response in
guard let value = response.value else {
print(response.error ?? "Unknown error")
return
}
print(value.subscriptions)
}
}
That produced:
[Subscription(batchUserId: 23, batchName: "demo batch", courseExtensions: [CourseExtension(id: 31, amount: "3500.0", courseId: 1, isActive: true)])]
By the way, I notice that your dates are in the format of MMM d yyyy. Would you like to convert those to Date objects? If so, you can use a decoder that specifies a date formatter like so:
let decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "MMM d yyyy"
decoder.dateDecodingStrategy = .formatted(formatter)
return decoder
}()
Then you can define startDate and endDate to be Date objects. Then when you present these dates in the UI, you can use a DateFormatter to show a nice localized rendition of the date, rather than just the fixed, ugly MMM d yyyy format.
To present a date in the UI, you’d then do something like:
let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
return formatter
}()
And then:
label.text = dateFormatter.string(from: date)
And a English speaker in the US will see:
Apr 15, 2019
A US-based Spanish speaker will see:
abr. 15, 2019
A Spanish speaker in Spain will see:
15 abr 2019
Bottom line, the user will see dates in the format that they expect them, rather than being hardcoded in some specific US English format. And you also have the choice to use .long format where space allows (e.g. “April 15, 2019”) or .short where you’re really tight for space (e.g. “04/15/19”). Just pick the dateStyle that suits your particular needs.

Trying to convert Firebase timestamp to NSDate in Swift

I'm trying to use Firebase timestamps in a Swift app. I'd like to store them in my Firebase, and use them as native NSDate objects in my app.
The docs say they are unix epoch time, so I've tried:
NSDate(timeIntervalSince1970:FirebaseServerValue.timestamp)
with no luck.
This:
FirebaseServerValue.timestamp
returns
0x00000001199298a0
according to the debugger. What is the best way to pass these timestamps around?
ServerValue.timestamp() works a little differently than setting normal data in Firebase. It does not actually provide a timestamp. Instead, it provides a value which tells the Firebase server to fill in that node with the time. By using this, your app's timestamps will all come from one source, Firebase, instead of whatever the user's device happens to say.
When you get the value back (from a observer), you'll get the time as milliseconds since the epoch. You'll need to convert it to seconds to create an NSDate. Here's a snippet of code:
let ref = Firebase(url: "<FIREBASE HERE>")
// Tell the server to set the current timestamp at this location.
ref.setValue(ServerValue.timestamp())
// Read the value at the given location. It will now have the time.
ref.observeEventType(.Value, withBlock: {
snap in
if let t = snap.value as? NSTimeInterval {
// Cast the value to an NSTimeInterval
// and divide by 1000 to get seconds.
println(NSDate(timeIntervalSince1970: t/1000))
}
})
You may find that you get two events raised with very close timestamps. This is because the SDK will take a best "guess" at the timestamp before it hears back from Firebase. Once it hears the actual value from Firebase, it will raise the Value event again.
For me in swift 5 use in another class:
import FirebaseFirestore
init?(document: QueryDocumentSnapshot) {
let data = document.data()
guard let stamp = data["timeStamp"] as? Timestamp else {
return nil
}
let date = stamp.dateValue()
}
This question is old, but I recently had the same problem so I'll provide an answer.
Here you can see how I am saving a timestamp to Firebase Database
let feed = ["userID": uid,
"pathToImage": url.absoluteString,
"likes": 0,
"author": Auth.auth().currentUser!.displayName!,
"postDescription": self.postText.text ?? "No Description",
"timestamp": [".sv": "timestamp"],
"postID": key] as [String: Any]
let postFeed = ["\(key)" : feed]
ref.child("posts").updateChildValues(postFeed)
The particularly relevant line of code is "timestamp": [".sv": "timestamp"],
This saves the timestamp as a double in your database. This is the time in milliseconds so you need to divide by 1000 in order to get the time in seconds. You can see a sample timestamp in this image.
To convert this double into a Date I wrote the following function:
func convertTimestamp(serverTimestamp: Double) -> String {
let x = serverTimestamp / 1000
let date = NSDate(timeIntervalSince1970: x)
let formatter = DateFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .medium
return formatter.string(from: date as Date)
}
This gives a timestamp that looks like this:
You will get the right time if you use:
let timestamp = FIRServerValue.timestamp()
let converted = NSDate(timeIntervalSince1970: timestamp / 1000)
let dateFormatter = NSDateFormatter()
dateFormatter.timeZone = NSTimeZone.localTimeZone()
dateFormatter.dateFormat = "hh:mm a"
let time = dateFormatter.stringFromDate(converted)
let serverTimeStamp = ServerValue.timestamp() as! [String:Any]
Store in Firebase something like [ktimeStamp:timestamp as AnyObject]
than after you convert in seconds using Firebase Server Time:
let timestampDate = NSDate(timeIntervalSince1970: Double(timestamp as! NSNumber)/1000)
Firestore has an API for this --> -(NSDate *)dateValue
For example, if you have saved(set) a new document with a field "createdAtDate"
NSDictionary *dataToBeSaved = #{
//Tell the server to save FIRTimestamps when the document is created
#"createdAtDate":[FIRFieldValue fieldValueForServerTimestamp],
#"lastModifiedDate":[FIRFieldValue fieldValueForServerTimestamp],
//Other fields
#"userName":#"Joe Blow"
}
[myFirReference setData:[dataToBeSaved]
options:[FIRSetOptions merge]
completion:^(NSError* error) {
}
You can get back this information either with a get query or via setting a listener. When you have the snapshot back, just access the dates you saved and convert to NSDate.
NSDate *date1 = [snapshot.data[#"createdAtDate"] dateValue];
NSDate *date2 = [snapshot.data[#"lastModifiedDate"] dateValue];
There will be a slight loss in precision, but as most people use dates for data synchronization or sorts, I can't think of a case where the loss of precision would be an issue.
You can create a new transformer for ObjectMapper,
import Foundation
import ObjectMapper
class FirebaseDateTransform: TransformType {
public typealias Object = Date
public typealias JSON = Double
open func transformFromJSON(_ value: Any?) -> Date? {
if let millisecondsSince1970 = value as? Double {
return Date(timeIntervalSince1970: millisecondsSince1970 / 1000.0)
}
return nil
}
open func transformToJSON(_ value: Date?) -> Double? {
if let date = value {
return Double(date.timeIntervalSince1970) * 1000.0
}
return nil
}
}
Gist
Here is some code, based on alicanbatur's answer, that allows a date to be a Double or a server timestamp, and yet still work within an object mapping layer such as ObjectMapper.
enum FirebaseDate {
case date(Date)
case serverTimestamp
var date: Date {
switch self {
case .date(let date):
return date
case .serverTimestamp:
return Date()
}
}
}
class FirebaseDateTransform: TransformType {
public typealias Object = FirebaseDate
public typealias JSON = Any
open func transformFromJSON(_ value: Any?) -> FirebaseDate? {
switch value {
case let millisecondsSince1970 as Double:
let date = Date(millisecondsSince1970: millisecondsSince1970)
return .date(date)
case is [AnyHashable: Any]?:
return .serverTimestamp
default:
return nil
}
}
open func transformToJSON(_ value: FirebaseDate?) -> Any? {
switch value {
case .date(let date)?:
return date.millisecondsSince1970
case .serverTimestamp?:
return ServerValue.timestamp()
default:
return nil
}
}
}
You can get a date approximation from Firebase. For example if you're trying to change a firebase user's creation date (a Timestamp) to a Date:
user.creationDate.dateValue()
Swift 4 and updated Firebase library variation of Katfang's answer:
let currentTimeStamp: TimeInterval?
let ref = Database.database().reference().child("serverTimestamp")
ref.setValue(ServerValue.timestamp())
ref.observe(.value, with: { snap in
if let t = snap.value as? TimeInterval {
print(t/1000)
currentTimeStamp = t/1000
}
})

Resources