How I can get date from Message kit in Swift firebase - ios

I'm working on message Kit and I want to get the date from the message kit, But I'm getting an error while used it. I've chatDashboardView Controller and I've defined the computed property (getFormatter) and above the chatdashboard I've defined the Message struct.
public static var getFormatter: String{
let dformatter = DateFormatter()
dformatter.dateFormat = "dd/MM/yyy HH:mm"
let dateToString = dformatter.string(from: Date())
return dateToString
}
struct Message: MessageType{
public var sender: SenderType
public var messageId: String
public var sentDate: Date
public var kind: MessageKind
}
In networking class want to access this getFormatter property its saying me (Cannot call value of non-function type 'String') this is the error see the code and guide me thanks
func getAllMessagesForConverstion(id:String,compltion:#escaping(Result<[Message],Error>) -> Void){
checkMessageUSer(id) { (user) in
guard let users = user else {print("not getting the users sorry on getllMessage");return}
let messages : [Message] = users.compactMap({dic in
guard let content = dic["content"] as? String,
let dateString = dic["date"] as? String,
let messageId = dic["id"] as? String,
let isRead = dic["is_read"] as? String,
let senderEmail = dic["sender_email"] as? String,
let typeMessage = dic["type"] as? String,
let name = dic["name"] as? String,
let dateData = ChatDashboard.getFormatter(from: dateString) else
{return nil}
let senderData = Sender(photo: "", senderId: senderEmail, displayName: name)
return Message(sender: senderData, messageId: messageId, sentDate: "", kind: .text(content))
//return Message(sender: senderData, messageId: messageId, sentDate: , kind: .text(content))
})
}
}

Your getFormatter computed property is a string, which is a string format of today (Date())
Seeing what you are trying to do, you need to change the getFormatter like below (better if you could rename it to something like formatter since it is a property not a method)
public static var formatter: DateFormatter {
let dformatter = DateFormatter()
dformatter.dateFormat = "dd/MM/yyy HH:mm"
return dformatter
}
then you can access it and use to format your date string like below in getAllMessagesForConverstion
let dateData = ChatDashboard.formatter.date(from: dateString)

Related

How can I create a UITableView for each property in a model?

I have a struct that looks something like this:
internal class RemoteProfileModel: Decodable {
let userId: String
let company: String
let email: String
let firstName: String
let lastName: String
let department: String
let jobTitle: String
let pictureUri: URL?
let headerUri: URL?
let bio: String
let updatedDate: Date
}
I need to list out these properties in a UITableView. I also need to use different cell types for some of the properties.
I'm thinking perhaps I should convert this struct to a dictionary of key/value pairs, and use the key to determine the cell type.
Is this possible? Is there another way to achieve this? I am unsure if it possible to convert a struct to a dictionary so am not sure this is the best way?
to convert a class to a dictionary,
class RemoteProfileModel: Decodable {
let userId: String
let company: String
let email: String
let firstName: String
let lastName: String
let department: String
let jobTitle: String
let pictureUri: URL?
let headerUri: URL?
let bio: String
let updatedDate: Date
init() {
userId = "666"
company = "AAPL"
email = "hehe#163.com"
firstName = "user"
lastName = "test"
department = "guess"
jobTitle = "poor iOS"
pictureUri = URL(string: "wrong")
headerUri = URL(string: "none")
bio = "China"
updatedDate = Date()
}
func listPropertiesWithValues(reflect: Mirror? = nil) -> [String: Any]{
let mirror = reflect ?? Mirror(reflecting: self)
if mirror.superclassMirror != nil {
self.listPropertiesWithValues(reflect: mirror.superclassMirror)
}
var yourDict = [String: Any]()
for (index, attr) in mirror.children.enumerated() {
if let property_name = attr.label {
//You can represent the results however you want here!!!
print("\(index): \(property_name) = \(attr.value)")
yourDict[property_name] = attr.value
}
}
return yourDict
}
}
Call like this:
let profile = RemoteProfileModel()
profile.listPropertiesWithValues()
In Swift Debugging and Reflection,
A mirror describes the parts that make up a particular instance

iOS error 'Invalid type in JSON write (FIRTimestamp)'

I am trying to map my data to Model.
Where I am using Firestore snapshot listener, to get data.
here, I am getting data and mapping to "User" model that;
do{
let user = try User(dictionary: tempUserDic)
print("\(user.firstName)")
}
catch{
print("error occurred")
}
Here is my Model:
struct User {
let firstName: String
// var lon: Double = 0.0
// var refresh:Int = 0
// var createdOn: Timestamp = Timestamp()
}
//Testing Codable
extension User: Codable {
init(dictionary: [String: Any]) throws {
self = try JSONDecoder().decode(User.self, from: JSONSerialization.data(withJSONObject: dictionary))
}
private enum CodingKeys: String, CodingKey {
case firstName = "firstName"
}
}
Correct me if I am wrong.
Crashing because I am getting "Timestamp" in data.
Data getting from listener :
User Dictionary:
[\"firstName\": Ruchira,
\"lastInteraction\": FIRTimestamp: seconds=1576566738 nanoseconds=846000000>]"
How to map "Timestamp" to Model?
Tried "CodableFirstore" https://github.com/alickbass/CodableFirebase
An approach is to create an extension to type Dictionary that coverts a dictionary to any other type, but automatically modifies Date and Timestamp types to writeable JSON strings.
This is the code:
extension Dictionary {
func decodeTo<T>(_ type: T.Type) -> T? where T: Decodable {
var dict = self
// This block will change any Date and Timestamp type to Strings
dict.filter {
$0.value is Date || $0.value is Timestamp
}.forEach {
if $0.value is Date {
let date = $0.value as? Date ?? Date()
dict[$0.key] = date.timestampString as? Value
} else if $0.value is Timestamp {
let date = $0.value as? Timestamp ?? Timestamp()
dict[$0.key] = date.dateValue().timestampString as? Value
}
}
let jsonData = (try? JSONSerialization.data(withJSONObject: dict, options: [])) ?? nil
if let jsonData {
return (try? JSONDecoder().decode(type, from: jsonData)) ?? nil
} else {
return nil
}
}
}
The .timestampString method is also declared in an extension for type Date:
extension Date {
var timestampString: String {
Date.timestampFormatter.string(from: self)
}
static private var timestampFormatter: DateFormatter {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = TimeZone(identifier: "UTC")
return dateFormatter
}
}
Usage, like in the case of the question:
let user = tempUserDict.decodeTo(User.self)
I solved this by converting the FIRTimestamp fields to Double (seconds) so the JSONSerialization could parse it accordingly.
let items: [T] = documents.compactMap { query in
var data = query.data() // get a copy of the data to be modified.
// If any of the fields value is a `FIRTimestamp` we replace it for a `Double`.
if let index = (data.keys.firstIndex{ data[$0] is FIRTimestamp }) {
// Convert the field to `Timestamp`
let timestamp: Timestamp = data[data.keys[index]] as! Timestamp
// Get the seconds of it and replace it on the `copy` of `data`.
data[data.keys[index]] = timestamp.seconds
}
// This should not complain anymore.
guard let data = try? JSONSerialization.data(
withJSONObject: data,
options: .prettyPrinted
) else { return nil }
// Make sure your decoder setups the decoding strategy to `. secondsSince1970` (see timestamp.seconds documentation).
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
return try? decoder.decode(T.self, from: data)
}
// Use now your beautiful `items`
return .success(items)

Firebase print snapshot works fine and fetches the dictionary but when tried to access individual field data, returns nil for certain fields

I have a problem accessing values from certain fields in my firebase database. Right now this is how my structure looks in firebase:
messages:
messageId:
fromId:
text:
timestamp:
toId:
I am able to successfully upload the data to firebase when a user inputs a message to another user. And I am also able to successfully print the snapshot. But when I set the dictionary values and access it, only "fromId" and "toId" works but "timestamp" and "text" returns a nil value.
Pretty sure there is some sort of a wrong implementation in terms of taking the snapshot values and setting it. For your reference, I have included 3 files, one where the data model is defined, one where I upload data to firebase and one where I am trying to print it but I get nil.
The file where I am trying to print data but I get nil. Note: I am only getting nil when I am trying to print "text" and "timestamp" field values. "fromId" and "toId" works.
import UIKit
import Firebase
class MessagesController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
observeMessages()
}
var messages = [Message]()
func observeMessages(){
let ref = Database.database().reference().child("messages")
ref.observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as?
Dictionary<String, AnyObject>{
let message = Message(dictionary: dictionary)
print(message.text)
print(message.fromId)
print(message.toId)
print(timestamp)
}
})
}
}
This is how I am uploading the data to firebase using a handle send function once the user has entered some text in the text box
#objc func handleSend(){
let ref = Database.database().reference().child("messages")
let childRef = ref.childByAutoId()
let toId = user!.uid!
let fromId = Auth.auth().currentUser!.uid
let timestamp: Int = Int(NSDate().timeIntervalSince1970)
let values = ["fromId": fromId, "text":
inputTextField.text!, "timestamp": timestamp, "toId": toId] as
[String : Any]
childRef.updateChildValues(values)
}
Finally this is how I have declared my messages class:
class Message{
var fromId: String!
var text: String!
var timestamp: Int!
var toId: String!
init(dictionary: Dictionary<String, AnyObject>) {
if let text = dictionary["messageText"] as? String {
self.text = text
}
if let fromId = dictionary["fromId"] as? String {
self.fromId = fromId
}
if let toId = dictionary["toId"] as? String {
self.toId = toId
}
if let timestamp = dictionary["creationDate"] as? Int {
self.timestamp = timestamp
}
}
}
When I print message.fromID, I get the data results in the console but when I print message.text or message.timestamp I get
nil
nil
nil
So in summary snapshot works, fromId, toID fields also work but for some reason the data from the text and timestamp fields are returned as nil
your are accessing values from dictionary with invalid key use text instead of messageText and use timeSamp instead of creationDate. like below
class Message{
var fromId: String!
var text: String!
var timestamp: Int!
var toId: String!
init(dictionary: Dictionary<String, AnyObject>) {
if let text = dictionary["text"] as? String {
self.text = text
}
if let fromId = dictionary["fromId"] as? String {
self.fromId = fromId
}
if let toId = dictionary["toId"] as? String {
self.toId = toId
}
if let timestamp = dictionary["timestamp"] as? Int {
self.timestamp = timestamp
}
}
}

how to add information in an array with a class Swift

I've got a problem for adding some informations in an array.
My class Flights is define by the following :
class Flight{
let date: String
let type: String
let regi: String
let totalTime: String
let depTime: String
let depPlace: String
let arrTime: String
let arrPlace: String
init(from dat: String, _ typ: String, _ reg: String, _ totaltim: String, _ depTim: String, _ depPlac: String, _ arrTim: String, _ arrPlac: String) {
self.date = dat
self.type = typ
self.regi = reg
self.totalTime = totaltim
self.depTime = depTim
self.depPlace = depPlac
self.arrTime = arrTim
self.arrPlace = arrPlac
}}
In my main code I've got declare my array like this :
var datas: [Flight] = []
And finally I've this code to add some informations coming from firebase :
(I add some comment to show you what print() result)
if let user = Auth.auth().currentUser{
// user is connect
let ref = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
let ev = ref.child("flights").child(userID!)
ev.observe(.childAdded, with: { (snapshot) -> Void in
let flightKey = snapshot.key
ref.child("flights").child(userID!).child(flightKey).observeSingleEvent(of: .value) {(snapshot) in
let value = snapshot.value as? NSDictionary
let date = value?["Date"] as? String ?? "no date"
let type = value?["aircraft-model"] as? String ?? "no type"
let registration = value?["aircraft-registration"] as? String ?? "no callsign"
let totalTime = value?["TOTAL-TIME"] as? String ?? "no total Time"
let deppartTime = value?["departure-time"] as? String ?? "no departure Time"
let deppartPlace = value?["departure-place"] as? String ?? "no departure Place"
let arrivalTime = value?["arrival-time"] as? String ?? "no arrival Time"
let arrivalPlace = value?["arrival-place"] as? String ?? "no arrival Place"
print("Date : \(date) - type : \(type) - registration : \(registration) - Etc ...")// Give me exactly the value I requested
self.datas.append(Flight(from: date, type, registration, totalTime, deppartTime, deppartPlace, arrivalTime, arrivalPlace))
print(self.datas)// Give me "MyProjectName.Flight ...
}
})
}else{
// si non connecté alors DECONNEXION !!!!
fatalError("error ...")
}
So I don't understand why if I print the received value from firebase it work but if I print the array value which is completed by the firebase received value it didn't work ?
Thanks for your help !
Flyer-74
Welcome :)
I think all is as expected and you're just seeing this because Swift doesn't know how to describe your objects.
To fix this, you should implement the CustomStringConvertible protocol in your Flight class (https://developer.apple.com/documentation/swift/customstringconvertible)
So something like
extension Flight: CustomStringConvertible {
var description: String {
var description = ""
description.append("date: \(date)\n")
description.append("type: \(type)\n")
description.append("regi: \(regi)\n")
//and so on
return description
}
}
Should give you what you are looking for.
Hope that helps you
You can try to adopt CustomStringConvertible protocol
class Flight : CustomStringConvertible {
var description: String {
return "\(date) \(type)" // add here any variable you want it to be printed
}
let date: String
let type: String
let regi: String
let totalTime: String
let depTime: String
let depPlace: String
let arrTime: String
let arrPlace: String
init(from dat: String, _ typ: String, _ reg: String, _ totaltim: String, _ depTim: String, _ depPlac: String, _ arrTim: String, _ arrPlac: String) {
self.date = dat
self.type = typ
self.regi = reg
self.totalTime = totaltim
self.depTime = depTim
self.depPlace = depPlac
self.arrTime = arrTim
self.arrPlace = arrPlac
}
}
You could add a custom debug description for your object by adding an extension to Flight, and make it conform to the CustomDebugStringConvertible protocol. Conformance to this protocol requires that you provide a property: var debugDescription: String { get }. Inside this string is where you have full control over the debug values for your custom Object.
extension Flight: CustomDebugStringConvertible {
var debugDescription: String {
return "Date: \(date), Type: \(type), Registartion: \(regi)"
}
}

unexpectedly found nil while unwrapping an Optional value when trying to create object via constructor

I'm trying to learn swift and the concept of optionals is destroying me.
I have the code below and it keeps crashing when trying to create an Event object b/c eventDescription is finding a nil value. How can I get past this error and create my Event object with a description that is either nil or an empty string?
EventDao.getUpcoming() {
(upcomingEvents, error) -> Void in
if(upcomingEvents != nil) {
// Loop through all the events
for currentEvent in upcomingEvents! {
print(currentEvent)
let name = currentEvent["name"] as! NSString
let start = currentEvent["start"] as! NSString
let teamId = currentEvent["team_id"] as! Int
let eventDescription = currentEvent["description"] as? String
let eventId = currentEvent["id"] as! Int
// Format date format
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
let date = dateFormatter.dateFromString(start as String)
dateFormatter.dateStyle = NSDateFormatterStyle.ShortStyle
dateFormatter.timeStyle = NSDateFormatterStyle.ShortStyle
let formattedDate = dateFormatter.stringFromDate(date!)
self.getTeamInfo(teamId) {
(team, err) -> Void in
self.getMyResponseForEvent(eventId) {
(response, err) -> Void in
print(eventDescription)
var event: Event
if (response == nil) {
event = Event(name: name as String, team: team!, time: formattedDate, description: eventDescription! as! String, myResponse: "No Response") // ERRORS HERE WITH: fatal error: unexpectedly found nil while unwrapping an Optional value
} else {
...
print(currentEvent outputs:
{
"default_response" = {
id = 1;
label = "No Response";
};
description = "<null>";
end = "2016-01-19T19:00:43.000-06:00";
id = 1966;
location = Withers;
name = "Game 5 VS cat.png";
start = "2016-01-19T18:00:43.000-06:00";
"team_id" = 193;
timezone = "America/Chicago";
}
This is my Event.swift file:
import Foundation
class Event {
// MARK: Properties
var name: String
var team: Team
var time: String
var description: String?
var myResponse: String
// MARK: Initialization
init(name: String, team: Team, time: String, description: String, myResponse: String) {
self.name = name
self.team = team
self.time = time
self.description = description
self.myResponse = myResponse
}
}
Try something like this:
if let t = team where response == nil {
event = Event(name: name as String, team: t, time: formattedDate, description: eventDescription ?? "", myResponse: "No Response")
} else...
One of your optionals was nil. Either team! was nil, or eventDescription! was nil. You can use the "nil coalescing operator" ?? to try to unwrap eventDescription and substitute a blank string if it is nil.
BTW, you shouldn't need to conditionally cast eventDescription to String again, since you already did that when you created it, in this line:
let eventDescription = currentEvent["description"] as? String
At that point, it is either a String or it is nil. You don't need to cast it again.
Just about every time you see the error unexpectedly found nil while unwrapping an Optional value it is because you are force unwrapping an optional with !.

Resources