I'm trying to write a function that I can call on my iOS app to return me the current (or approximate) firebase server time.
I couldn't get any success with the following attempts. am I doing something wrong?
Obs.: The second one return an object of type FIRHTTPSCallableResult and I couldn't find a way to parse it, neither see the content into it to be sure if it worked.
exports.currentTime = functions.https.onRequest((req, res) => {
res.send({"timestamp":new Date().getTime()})
});
exports.currentTimeTwo = functions.https.onCall((data, context) => {
return {"timestamp":new Date().getTime()};
});
exports.currentTimeThree = functions.https.onRequest((req, res) => {
const data = {"timestamp":new Date().getTime()}
res.send(data)
});
iOS Code:
static func getServerTime(){
Functions.functions().httpsCallable("currentTimeTwo").call { (result, error) in
if let error = error as NSError? {
print("error \(error)")
if error.domain == FunctionsErrorDomain {
print("error domain: \(error.domain)")
let code = FunctionsErrorCode(rawValue: error.code)
print("error code: \(code)")
let message = error.localizedDescription
print("error message: \(message)")
let details = error.userInfo[FunctionsErrorDetailsKey]
print("details: \(details)")
}
}
print(result)
}
}
exports.currentTimeTwo = functions.https.onCall((data, context) => {
return {"timestamp":new Date().getTime()};
});
The function above is right. As Frank informed in the comments of my question, I was missing to access the property data from swift code result.
New Swift Code:
static func getServerTime(){
Functions.functions().httpsCallable("currentTimeTwo").call { (result, error) in
if let error = error as NSError? {
print("error \(error)")
if error.domain == FunctionsErrorDomain {
print("error domain: \(error.domain)")
let code = FunctionsErrorCode(rawValue: error.code)
print("error code: \(code)")
let message = error.localizedDescription
print("error message: \(message)")
let details = error.userInfo[FunctionsErrorDetailsKey]
print("details: \(details)")
}
}
let resultFireBase = result as! HTTPSCallableResult
print(result?.data)
}
}
}
Only the functions.https.onCall in your Cloud Functions code matches with the Functions.functions().httpsCallable in your Swift code, so the other ones are meaningless here.
When you call the httpsCallable, you get a FIRHTTPSCallableResult whose data property contains whatever your Cloud Function returns, as long as it's a valid JSON type, which your new Date().getTime() seems to be
Related
So I'm trying to get all documents from a specific collection from Firestore, but when stepping through the code, it skipped over the line and doesn't throw an error. I've used the exact same code in other parts of my app with success but it is not working here for some reason? Any help would be appreciated.
func getClientEmail() -> String? {
var clientEmail: String?
self.db.collection("Clients").getDocuments() { (querySnapshot, err) in
if let err = err {
print("error getting docs: \(err)")
} else {
for document in querySnapshot!.documents {
let result = Result {
try document.data(as: User.self)
}
switch result {
case .success(let client):
if let client = client {
if client.placementIDs[0] == self.user?.placementIDs[0] {
clientEmail = client.email
}
} else {
print("Document doesn't exist")
}
case .failure(let error):
print("Error decoding user: \(error)")
}
}
}
}
return clientEmail
}
After some testing, I moved this code to within the viewDidLoad() function and it worked... So I think it has something to do within it being wrapped in a function but not sure why, hope this information is helpful to anyone that might be able to fix this problem.
Thanks to Jay's comment, I managed to fix the problem by having clientEmail as a global variable and using a completion handler to assign the value in this function.
func getClientEmail() {
// Get email of client for corresponding contractor
db.collection("Clients").getDocuments(completion: { (querySnapshot, err) in
if let err = err {
print(err.localizedDescription)
return
} else {
for document in querySnapshot!.documents {
let result = Result {
try document.data(as: User.self)
}
switch result {
case .success(let client):
if let client = client {
if client.placementIDs[0] == self.user?.placementIDs[0] {
self.clientEmail = client.email
}
} else {
print("Document doesn't exist")
}
case .failure(let error):
print("Error decoding user: \(error)")
}
}
}
})
}
Calling a Cloud Function from an iOS app looks like this:
var functions = Functions.functions()
functions.httpsCallable(name).call(data){ (result, error) in
// Code here
}
How can I send an error from my Cloud Function, so it will get picked up in the above code? For example:
Cloud Function:
exports.joinGame = functions.https.onCall((data, context) => {
return Error("The game is full")
})
SwiftUI:
var functions = Functions.functions()
functions.httpsCallable("joinGame").call(data){ (result, error) in
print(error) // "The game is full"
}
I'm still not sure where do you want to return error... but here is some variants of possible handling:
#State private var error: Error?
var body: some View {
VStack {
Text("Wait for Error")
.onAppear {
functions.httpsCallable("joinGame").call(data){ (result, error) in
print(error) // "The game is full"
DispatchQueue.main.async {
self.error = error
}
}
}
if nil != error {
Text("Got error!") // << do anything with error here
}
}
.onChange(of: error) { newError in
// << or here
}
}
I'm currently trying to develop an ios application using Firestore, and when querying the database, I'm getting no results, as neither the if/else block execute. I'm wondering what is going wrong here...
db.collection("users").whereField("uid", isEqualTo: uid).getDocuments() { (querySnapshot, error) in
if let error = error {
print("Error getting documents: \(error.localizedDescription)")
} else {
for document in querySnapshot!.documents {
weight = document.data()["weight"]! as? Double
}
}
}
Database file structure
Update: I make a call to the database in an earlier method, and this properly returns the user's first name (when I add the weight, it also returns the correct value). But any subsequent calls fail to return anything. Hopefully that info helps.
I have the same Firestore structure like you, and this works for me:
func test() {
var accessLevel: Double?
let db = Firestore.firestore()
db.collection("users").whereField("uid", isEqualTo: UserApi.shared.CURRENT_USER_UID!).getDocuments() { (querySnapshot, error) in
if let error = error {
print("Error getting documents: \(error.localizedDescription)")
} else {
for document in querySnapshot!.documents {
accessLevel = document.data()["accessLevel"]! as? Double
print(accessLevel!)
}
}
}
}
Current uid:
// Actual logged-in User
var CURRENT_USER_UID: String? {
if let currentUserUid = Auth.auth().currentUser?.uid {
return currentUserUid
}
return nil
}
Hope it helps.
I hava determined authorization before actually trying to access the user's dateofBirth and biologicalSex. But it's work on simulator.but not on iphone and paired watch.
let birthdayType = HKQuantityType.characteristicType(forIdentifier: HKCharacteristicTypeIdentifier.dateOfBirth)
let biologicalSexType = HKQuantityType.characteristicType(forIdentifier: HKCharacteristicTypeIdentifier.biologicalSex)
let quantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)
let readDataTypes: Set<HKObjectType> = [quantityType!, birthdayType!, biologicalSexType!]
guard HKHealthStore.isHealthDataAvailable() == true else {
label.setText("not available")
return
}
let readDataTypes: Set<HKObjectType> = self.dataTypesToRead()
healthStore.requestAuthorization(toShare: nil, read: readDataTypes) { (success, error) -> Void in
if success == false {
self.displayNotAllowed()
}
}
var birthDay: Date! = nil
do {
birthDay = try self.healthStore.dateOfBirth()
} catch let error as NSError{
print("Either an error occured fetching the user's age information or none has been stored yet. \(error)")
return -1
}
var biologicalSex:HKBiologicalSexObject! = nil
do {
try biologicalSex = self.healthStore.biologicalSex()
}catch let error as NSError{
print("Failed to read the biologicalSex! \(error)")
}
This is a concurrency problem, not a HealthKit problem.
requestAuthorization may display a dialog and gives you the result in a background process.
You have to wait for the answer of requestAuthorization before you continue reading birthday and biologicalSex.
In one of my view controllers I have a function to establish a tcp connection , send and receive messages which is as follows.
func tcpConnection(host: NSString){
var client = TCPClient(addr: String(host) as String, port: Config.tcpPort)
let (success, err) = client.connect(timeout: Config.tcpConnectionTimeout)
log?.debug("Connected \(success)")
guard success else { log?.error("Cannot Connect \(err)"); return}
let (successMsg, failmsg) = client.send(str: self.jsonString)
let data = client.read(Config.expectedByteLength)
guard let d = data else { return }
guard let recievedMsg = String(bytes: d, encoding: NSUTF8StringEncoding) else { return }
self.recivedMsgFromServer = recievedMsg
log?.debug("Recieved msg\(recievedMsg)")
let (clientClosed, errormsg) = client.close()
guard clientClosed else { return }
}
I am using this piece of code twice in the same view controller. So I want to have a generic class for this function . Also I have many guards which I want to replace with a single try catch block.
Also after receiving a message I am doing different things in both the tcp connection functions.
Following is what I have tried until now.
class TcpService{
var jsonString : String = ""
func tcpConnection(host: NSString){
do {
var client = try TCPClient(addr: String(host) as String, port: Config.tcpPort)
let (success, err) = client.connect(timeout: Config.tcpConnectionTimeout)
log?.debug("Connected \(success)")
guard success else { log?.error("Cannot Connect \(err)"); return}
let (successMsg, failmsg) = client.send(str: self.jsonString)
let data = client.read(Config.expectedByteLength)
guard let d = data else { return }
guard let recievedMsg = String(bytes: d, encoding: NSUTF8StringEncoding) else { return }
log?.debug("Recieved msg\(recievedMsg)")
/*Do Something different in every viewController
//For Example
self.Info = Mapper<Info>().map(recievedMsg)
log?.debug("Status\(self.Info?.Status)")
*/
let (clientClosed, errormsg) = client.close()
guard clientClosed else { return }
} catch {
let fetchError = error as NSError
print(fetchError)
// Handle Error
}
}
}
When I try to call this function from the new class in my view controller it does not behave in the same way the function in the view controller did.
Is there anything I should change in the way I have created the class?
Any help will be appreciated as I am very new to swift. Thank you
This is a way your problem could be solved. Skip the custom error throwing part if you don't actually need it. The way to refactor it is using callback function, just send your function into this one, i.e.:
TCPService.connect("some_host", message: "your_message") { response in
Mapper<Info>().map(response)
}
Code:
import Foundation
extension NSError {
static func error(with localized: String) -> NSError {
return NSError(domain: "error", code: 2, userInfo: [NSLocalizedDescriptionKey: localized])
}
}
class TCPService {
static func connect(host: String, andSend message: String, onComplete: String -> ()) {
do {
let client = try TCPClient(addr: host, port: Config.tcpPort)
let (successfulConnection, error) = client.connect(timeout: Config.tcpConnectionTimeout)
log?.debug("Connected \(successfulConnection)")
guard successfulConnection else {
throw NSError.error(with: error)
log?.error("Cannot Connect \(error)")
return
}
let (successfullySent, failureMessage) = client.send(str: message)
let data = client.read(Config.expectedByteLength)
guard successfullySent,
let d = data,
receivedMessage = String(bytes: d, encoding: NSUTF8StringEncoding) else {
throw NSError.error(with: failureMessage)
return
}
log?.debug("Received msg\(receivedMessage)")
onComplete(receivedMessage)
let (clientClosed, errorMessage) = client.close()
guard clientClosed else {
throw NSError.error(with: errorMessage)
return
}
} catch let error as NSError {
let fetchError = error as NSError
print(fetchError)
// Handle errors
}
}
}