How would i get in correct way last record from the data base ? For now I am getting last record but i can not cast it. The warning says "Cast from '[Class]' to unrelated type 'Class' always fails". Is it other way to get properties from this result ?
let _context = DataBaseController.getContext()
let _fetchRequest:NSFetchRequest<Class> = Class.fetchRequest()
do {
let _allElements = try _context.count(for: _fetchRequest)
_fetchRequest.fetchLimit = 1
if _allElements == 1 {
_fetchRequest.fetchOffset = _allElements
} else {
_fetchRequest.fetchOffset = _allElements - 1
}
_fetchRequest.returnsObjectsAsFaults = false
do {
let _result = try DataBaseController.getContext().fetch(_fetchRequest) as! Ping
} catch {
print("Error \(error)")
}
} catch {
print("Error \(error)")
}
Thanks in advance!
A Core Data fetch returns always an array of managed objects
let _result = try DataBaseController.getContext().fetch(_fetchRequest) as! [Ping]
if !_result.isEmpty {
let lastItem to _result[0]
}
Related
The following is a function to add a listener to a query. Whenever a document is added/removed I make some changes on two arrays (one of the user Ids and one of the user details). As you can see I tried printing everything: I correctly receive the data whenever it is added/removed, I can retrieve the document ID I need but whenever I append it to the usersReqestedUIDs array it always prints it as empty, even if I try to append a random string in it. Why is that?
func addRequestedUsersSnapshot() {
let db = Firestore.firestore()
let userRef = db.collection("user").document(user.UID)
let userRequestedRef = userRef.collection("friends").whereField("status", isEqualTo: "request")
// First query to fetch all friendIDs
userRequestedRef.addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: (error!)")
return
}
snapshot.documentChanges.forEach { diff in
print("ids : (self.usersReqestedUIDs)")
print("type : (diff.type)")
if diff.type == .added {
print("doc id (diff.document.documentID)")
self.usersReqestedUIDs.append("hello")
print("added (diff.document.data())")
print("ids : (self.usersReqestedUIDs)")
self.fetchUserDetailsByUID(uid: diff.document.documentID) { result in
switch result {
case let .success(user):
self.usersReqestedDetails.append(user)
case let .failure(error):
print(error)
}
}
}
if diff.type == .removed {
print("removed (diff.document.data())")
self.usersReqestedDetails.removeAll(where: { $0.UID == diff.document.documentID })
self.usersReqestedUIDs.removeAll(where: { $0 == diff.document.documentID })
}
if diff.type == .modified {
print("modified (diff.document.data())")
}
}
}
}
I am transitioning from UIKit to SwiftUI in my app and am updating network calls to async await as well. I created a Large size widget that displays the weather for 7 airports. The airports identifiers are stored in an app group shared userdefatults container. I update the timeline every minute (just for testing, normally it would be every 20 minutes). Initially when the widget is selected and appears, all data is there and correct. After a timeline update the data updates, but not all the airports are returned and after two updates or so (not consistent), the screen goes blank. The userdefaults airports are updated from the main app and saved in the shared user defaults container and it calls WidgetCenter.shared.reloadAllTimelines. This is all working fine as I have another process that uses the same container for a small widget, but with only one airport returning data without the need for an appended array. If I remove the calls to empty the array, the data remains and doesn't go blank, but of course the array keeps appending. I've tried the removeAll() and [] to empty the array at different places in the code, but same result. I am trying to understand the flow in the async/await calls, but seem to be missing something here? Any help would be greatly appreciated. I've been googling and searching stack overflow for a month and don't really know how to solve this issue. Thanks in advance!
actor MetarService: NSObject, XMLParserDelegate, URLSessionDelegate, ObservableObject {
enum MetarFetcherError: Error {
case invalidServerResponse
case missingData
}
#Published var metarArray = [String]()
#Published var metarDataModel: [MetarDataModel] = []
var tempDataModel: [MetarDataModel] = []
func fetchMetars(metarAPTs: String) async throws -> [MetarDataModel] {
let wxUrl = URL(string: "https://www.aviationweather.gov/adds/dataserver_current/httpparam?dataSource=metars&requestType=retrieve&format=xml&hoursBeforeNow=3&mostRecent=true&stationString=" + metarAPTs)!
let (data, response) = try await URLSession.shared.data(from: wxUrl)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw MetarFetcherError.invalidServerResponse
}
guard let xml = SWXMLHash.parse(data) as XMLIndexer? else {
throw MetarFetcherError.missingData
}
noDataResponse = (xml["response"]["data"].element?.attribute(by: "num_results")?.text) ?? "0"
if noDataResponse == "1" && (xml["response"]["data"]["METAR"]["observation_time"].element?.text) != nil {
if (xml["response"]["data"]["METAR"]["station_id"].element?.text) != nil {
myairport = xml["response"]["data"]["METAR"]["station_id"].element!.text
} else {
myairport = "MSNG"
}
if (xml["response"]["data"]["METAR"]["flight_category"].element?.text) != nil {
myfltcat = xml["response"]["data"]["METAR"]["flight_category"].element!.text
} else {
myfltcat = "MISNG"
}
switch myfltcat {
case "VFR":
mymetarImage = "sun.max.circle.fill"
case "MVFR":
mymetarImage = "cloud.sun.circle.fill"
case "IFR":
mymetarImage = "cloud.fog.circle.fill"
case "LIFR":
mymetarImage = "smoke.circle.fill"
default:
mymetarImage = "person.crop.circle.badge.questionmark"
}
if (xml["response"]["data"]["METAR"]["observation_time"].element?.text) != nil {
myobstime = xml["response"]["data"]["METAR"]["observation_time"].element!.text as NSString
if myobstime.length < 16 {
myobstime = "MISNG"
} else {
myobstime = myobstime.substring(with: NSRange(location: 11, length: 5)) as NSString
}
}
if (xml["response"]["data"]["METAR"]["visibility_statute_mi"].element?.text) != nil {
myvis = xml["response"]["data"]["METAR"]["visibility_statute_mi"].element!.text
let intVis = (myvis as NSString) .integerValue
myvis = String(intVis) + "SM"
} else {
myvis = "0"
}
if (xml["response"]["data"]["METAR"]["wind_dir_degrees"].element?.text) != nil {
mywinddir = xml["response"]["data"]["METAR"]["wind_dir_degrees"].element!.text
if mywinddir.contains("VRB") {
mywinddir = "VRB"
} else
if mywinddir.count <= 2 && mywinddir.count > 0 {
mywinddir = "0" + mywinddir
}
} else {
mywinddir = "MISNG"
}
if (xml["response"]["data"]["METAR"]["wind_speed_kt"].element?.text) != nil {
mywindspd = xml["response"]["data"]["METAR"]["wind_speed_kt"].element!.text
if mywindspd == "0" {
mywind = "Calm"
} else if mywindspd.count == 1 {
mywindspd = "0" + mywindspd
mywind = mywinddir + "/" + mywindspd + "KT"
} else if mywindspd.count > 1 {
mywind = mywinddir + "/" + mywindspd + "KT"
}
} else {
mywind = "MISNG"
}
}
self.tempDataModel.append(MetarDataModel(metarImage: mymetarImage, mairport: myairport, mobstime: myobstime as String, mfltcat: myfltcat, mvis: myvis, mwind: mywind))
self.metarDataModel = self.tempDataModel
tempDataModel = []
return metarDataModel
}
func readMetarApts() -> [String] {
let defaults = UserDefaults(suiteName: "group.userdefaults.shared.FRAT")!
if ((defaults.value(forKey: "icaoIdent") as! String).isEmpty) {
defaultairport = "KSFO"
} else {
defaultairport = defaults.value(forKey: "icaoIdent") as! String
}
wxAirport1 = defaults.value(forKey: "wxAirport1") as! String
wxAirport2 = defaults.value(forKey: "wxAirport2") as! String
wxAirport3 = defaults.value(forKey: "wxAirport3") as! String
wxAirport4 = defaults.value(forKey: "wxAirport4") as! String
wxAirport5 = defaults.value(forKey: "wxAirport5") as! String
wxAirport6 = defaults.value(forKey: "wxAirport6") as! String
metarArray.append(defaultairport)
metarArray.append(wxAirport1)
metarArray.append(wxAirport2)
metarArray.append(wxAirport3)
metarArray.append(wxAirport4)
metarArray.append(wxAirport5)
metarArray.append(wxAirport6)
metarArray = metarArray.sorted()
let returnArray = metarArray
metarArray = []
return returnArray
}// end of readAirports function
nonisolated func getAirports() -> ([MetarDataModel] ){
// transData = []
Task{
let tempArray = await readMetarApts()
for apts in tempArray {
let zData = try await self.fetchMetars(metarAPTs: apts)
if zData .isEmpty {
let errorData = MetarDataModel(metarImage: "sun.max.circle.fill", mairport: "DATA", mobstime: "CHK", mfltcat: "MSNG", mvis: "WiFi", mwind: "")
tempData = [errorData]
} else {
transData.append(contentsOf: zData)
tempData = transData
} // else Closure
} //Task Closure
//transData.removeAll()
} // apts in tempArray Closure
tempData = transData
// transData = []
return tempData.sorted()
} // end of getAirports function
} // end of MetarService Class
I have tried different solutions found on stack overflow, reddit, medium and others. But no matter what approach I take, if I try and empty the appended array in preparation for the next timeline update, it will always eventually return without data. At first I thought it was the app group shared container losing reference as I got the much debated 'kCFPreferencesAnyUser, ByHost: Yes, Container: (null)): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd' in the log, but Apple says this does not indicate that particular condition? And, I use the container elsewhere with no issues. I am new to the async await and the new URLSession.shared.data(from: ) and maybe I'm not understanding the flow and timing of how the data is fetched and returned? I just need to append the array, display the data, then empty the array and wait for the next timeline to fetch new data. I've put the removeAll() and tried [] as an alternative in many different places in my code (at the start of the function and at the end of the function). Stumped!!
I am having an issue where I run this function:
static func getAllJokes(reset: Bool, completion: #escaping () -> Void) {
Utilities.getDadJokes(reset: reset) {
print("Dad Jokes Pass, array = \(DadJokes.dadJokes)")
Utilities.getAssistantJokes(reset: reset) {
print("Assistant Jokes Pass, array = \(AssistantJokes.assistantJokes)")
Utilities.getKnockKnockJokes(reset: reset) {
print("Knock Knock Jokes Pass, array = \(KnockKnockJokes.knockKnockJokes)")
Utilities.getRandomJokes(reset: reset) {
print("Random Jokes Pass, array = \(RandomJokes.randomJokes)")
completion()
}
}
}
}
}
getDadJokes:
db.collection("jokes").document("Dad Jokes").addSnapshotListener { document, error in
//check for error
if error == nil {
//check if document exists
if document != nil && document!.exists {
if let JokeNum = document!.get("JokeNum") as? Int {
self.countDadJokes = JokeNum
UserDefaults.standard.setValue(JokeNum, forKey: "countDadJokes")
print("DadJokeNum = \(self.countDadJokes)")
}
// var DadJokes.dadJokes.count = 1
print("count = \(DadJokes.dadJokes.count)/\(self.countDadJokes)")
print("countDadJoke = \(self.countDadJokes)")
self.jokes.removeAll()
if reset == true {
DadJokes.dadJokes.removeAll()
}
// for _ in 0...self.countDadJokes {
while DadJokes.dadJokes.count <= self.countDadJokes {
// print("count = \(DadJokes.dadJokes.count)/\(self.countDadJokes)")
if let Joke = document!.get("\(DadJokes.dadJokes.count + 1)") as? String {
print("DadJokeNum = \(self.countDadJokes)")
if Utilities.jokes.contains("\(Joke) - From Dad Jokes") {}else {
Utilities.jokes.append("\(Joke) - From Dad Jokes")
DadJokes.dadJokes.append(Joke)
UserDefaults.standard.set(DadJokes.dadJokes, forKey: defaults.dadJokes.rawValue)
Utilities.updateJokesDefaults()
print("countDadJokesSaved = \(DadJokes.dadJokes.count)")
print("DadJokesSaved = \(DadJokes.dadJokes)")
}
print("Dad Joke: \(Joke)")
//print("count = \(DadJokes.dadJokes.count)/\(self.countDadJokes)")
if DadJokes.dadJokes.count == self.countDadJokes {
completion()
}
}
}
}
}
}
runs fine, but then when I run getAssistantJoke (the same thing):
print("assistant get running")
db.collection("jokes").document("Assistant Jokes").addSnapshotListener { document, error in
//check for error
if error == nil {
//check if document exists
if document != nil && document!.exists {
if let JokeNum = document!.get("JokeNum") as? Int {
self.countAssistantJokes = JokeNum
UserDefaults.standard.setValue(JokeNum, forKey: defaults.countAssistantJokes.rawValue)
print("DadJokeNum = \(self.countDadJokes)")
}
// var DadJokes.dadJokes.count = 1
print("count = \(AssistantJokes.assistantJokes.count)/\(self.countAssistantJokes)")
print("countAssistantJokes = \(self.countAssistantJokes)")
if reset == true {
AssistantJokes.assistantJokes.removeAll()
}
// for _ in 0...self.countDadJokes {
while AssistantJokes.assistantJokes.count <= self.countAssistantJokes {
// print("count = \(AssistantJokes.assistantJokes.count)/\(self.countAssistantJokes)")
if let Joke = document!.get("\(DadJokes.dadJokes.count + 1)") as? String {
print("AssistantJokeNum = \(self.countAssistantJokes)")
if Utilities.jokes.contains("\(Joke) - From Assistant Jokes") {}else {
Utilities.jokes.append("\(Joke) - From Assistant Jokes")
AssistantJokes.assistantJokes.append(Joke)
UserDefaults.standard.set(AssistantJokes.assistantJokes, forKey: defaults.assistantJokes.rawValue)
Utilities.updateJokesDefaults()
print("countAssistantJokesSaved = \(AssistantJokes.assistantJokes.count)")
print("AssistantJokesSaved = \(AssistantJokes.assistantJokes)")
}
print("Assistant Joke: \(Joke)")
// print("count = \(AssistantJokes.assistantJokes.count)/\(self.countAssistantJokes)")
if AssistantJokes.assistantJokes.count == self.countAssistantJokes {
completion()
}
}
}
}
}
}
}
It stops at firebase query. The documents and everything exist, therefor, what's the issue? Everything exists and I am truly stumped. My goal is to retrieve a ton of data and display a random subject. I feel like this should be easier than it is.
Here is my firebase setup:
I feel like the main problem here is the way in which you are modelling the data in Firestore.
You have a collection called jokes but then a document called Assistant Jokes, Dad Jokes, etc...
I think a much better structure would be for each document to be a single joke inside the jokes collection. Use an "auto id" for each joke and then put a type inside the joke document.
The fact that you have to maintain a JokeNum next to your list of jokes is a sign that something is wrong.
By structuring this way you would then be able to get all jokes or filter the jokes by a particular type etc...
Your code is super complex at the moment but I think the best approach is to structure your data in a way that helps your code.
I am new to Firebase and Swift. My previous question was very vague due to a misunderstanding on my part. In a class named "A" for example I am trying to create an object of class "B" that contains the fetchARImageTargets function that I have below. I am trying to assign the array ARImageTargets to a var in class "A" however, the listAll completion is not returned in time, which results in the var being empty. Is there a way that I can edit my function or class to avoid the var being set prematurely?
let ARImageTargetStorageRef = Storage.storage().reference().child("ImageTargets")
self.fetchARImageTargets(ref: ARImageTargetStorageRef)
func fetchARImageTargets(ref: StorageReference) {
ref.listAll { (result, error) in
if let error = error {
print(error)
}
for prefix in result.prefixes {
self.fetchARImageTargets(ref: prefix)
}
for item in result.items {
item.getMetadata { (metadata, error) in
if let error = error {
print(error)
} else {
var imageTarget = ARImageTarget()
item.downloadURL(completion: { (url, error) in
imageTarget.ImageURL = url
})
imageTarget.Id = metadata?.customMetadata?["Id"] as String?
let width = metadata?.customMetadata?["PhysicalWidth"] as String?
imageTarget.PhysicalWidth = CGFloat(truncating: NumberFormatter().number(from: width!)!)
self.ARImageTargets.append(imageTarget)
}
}
}
}
}
checking undefined value before retrieving from parse
I am making a simple app, and implementing the userInformation part. The user can edit his info, but I have trouble that if user doesn't put any info, it will crash when I try to retrieve data from an undefined column.
This is my code to retrieve the user data. If there is data to parse it won't crash, otherwise it will.
var query = PFQuery(className: "Note")
query.getObjectInBackgroundWithId("kg8KhAWCms", block: {
(obj, error)in
if let score = obj! as? PFObject {
print(score.objectForKey("title"))
var nickname = (score.objectForKey("title")) as! String
self.nickNameLabel.text = nickname
} else {
print(error)
}
})
I tried this code as well, but it has error which is binary operator '!=' cannot be applied to operands of type 'String' and 'NiLiteralConvertible'
var query = PFQuery(className: "Note")
query.getObjectInBackgroundWithId("kg8KhAWCms", block: {
(obj, error)in
if let obj = obj! as? PFObject {
print(obj.objectForKey("title"))
var nickname = (obj.objectForKey("title")) as! String
if (nickname != nil) {
self.nickNameLabel.text = nickname
}else{
self.nickNameLabel.text = "you don't have a nick name"
}
} else {
print(error)
}
})
So I am asking how can I handle retrieving undefined value before crash? (please write full code for me)
Is there a way I can check undefined value or specific value in column before I retrieve it?
///like this
if (value in parse == "ABC") {
print("yes")
}