On an app Im working on, users are able to upload projects by selecting an "upload project" button. Selecting this button triggers a segue to a screen where the user can now input their information for the project. Once the information is filled in and users select "upload", the screen dismisses itself, returning the user to their profile. The problem is that when a user returns to the profile screen, the project they just uploaded does not show in the collection view. it only shows the projects that were downloaded when the user first viewed their profile.
To try and fix this, I added a snapshot listener. The problem with this is: It loads the new project in, but in return, it loads every project twice. How can I set it up to where only the new project is appended, instead of every project being added again?
func setUserProjects(){
let uid = Auth.auth().currentUser?.uid
let db = Firestore.firestore()
let docRef = db.collection("projects")
docRef.whereField("uid", isEqualTo: uid!).getDocuments { (snapshot, err) in
if let err = err {
print("Error:\(err)")
} else {
for document in snapshot!.documents {
var dictionary = document.data()
let project = Project(dictionary: dictionary as [String: AnyObject])
project.id = document.documentID
self.userProjects.append(project)
self.userProjects.uniqued()
}
}
self.projectsCollectionView.reloadData()
}
docRef.whereField("uid", isEqualTo: uid!).addSnapshotListener { snapshot, err in
if let err = err {
print("Error:\(err)")
} else {
for document in snapshot!.documents {
var dictionary = document.data()
let project = Project(dictionary: dictionary as [String: AnyObject])
project.id = document.documentID
///only append the project if it's not already added
if !self.userProjects.contains(project) {
self.userProjects.append(project)
}
}
}
}
}
Related
I am trying to retrieve the data set in an array in one function, in a different function. I appended the data i want into the OtherUserArray in the
createNewConversation function. However when i try the retrieve this data in the sendMessage function, it prints an empty array.
import MessageKit
import InputBarAccessoryView
import FirebaseAuth
import Firebase
import Foundation
final class DatabaseManager {
static let shared = DatabaseManager()
var foundOtherUser:String?
var OtherUserArray = [String]()
}
extension DatabaseManager {
// create a new conversation with target user, uid and first sent message
public func createNewConversation(with otherUserUid: String, name: String, firstMessage: Message, completion: #escaping(Bool) -> Void) {
let db = Firestore.firestore()
let CurrentUser = Auth.auth().currentUser?.uid
let defaults = UserDefaults.standard
defaults.set(CurrentUser, forKey: "uid")
guard let currentUid = UserDefaults.standard.string(forKey: "uid"), let currentName = UserDefaults.standard.string(forKey: "usersFullname") else {
return
}
let messageDate = firstMessage.sentDate
let dateString = MessageViewController.dateFormatter.string(from: messageDate)
var message = ""
let conversationId = firstMessage.messageId
let newConversationData: [String:Any] = [
"id": conversationId,
"other_user-uid": otherUserUid,
"name": name,
"latest-message": [
"date": dateString,
"message": message,
"is-read": false
]
]
// save other user uid to global var
OtherUserArray.append(otherUserUid)
}
}
}
public func sendMessage(to conversation: String, name: String, newMessage: Message, completion: #escaping(Bool) -> Void) {
// update the latest message
print(self.OtherUserArray)
}
The problem is not where you access self.OtherUserArray, but when you access it.
Data is loaded from Firebase asynchronously, while the rest of you code typically runs synchronously. By the time the print(self.OtherUserArray) in your sendMessage runs, the OtherUserArray.append(otherUserUid) hasn't been executed yet, so the array in empty. You can most easily verify this by running the code in a debugger and setting breakpoints on those like, or by adding print statements and checking the order of their output.
For examples of this, as well as how to solve the problem, see these previous questions:
Why I couldn't assign fetched values from Firestore to an array in Swift?
Is Firebase getDocument run line by line?
Storing asynchronous Cloud Firestore query results in Swift
How do I save data from cloud firestore to a variable in swift?
Why are print statements not executing in correct order?? Am I crazy or is Xcode?
Get all documents at once in a completion handler with getDocuments in Firestore
I've setup Firestore DB and wanted to test it. However I was unable neither read or wright data to it. Here is what console shows on connection status to Firebase.
Debug Console:
2020-11-19 17:58:49.802849+0300 Demo [813:165300] 6.34.0 - [Firebase/Analytics][I-ACS023007] Analytics v.60900000 started
2020-11-19 17:58:49.803819+0300 Demo [813:165300] 6.34.0 - [GoogleUtilities/AppDelegateSwizzler][I-SWZ001014] App Delegate does not conform to UIApplicationDelegate protocol.
2020-11-19 17:58:49.804498+0300 Demo [813:165300] 6.34.0 - [Firebase/Analytics][I-ACS023008] To enable debug logging set the following application argument: -FIRAnalyticsDebugEnabled (see https://help.apple.com/xcode/mac/8.0/#/dev3ec8a1cb4)
2020-11-19 17:58:50.586054+0300 Demo [813:165306] 6.34.0 - [Firebase/Analytics][I-ACS800023] No pending snapshot to activate. SDK name: app_measurement
2020-11-19 17:58:51.138985+0300 Demo [813:165306] 6.34.0 - [Firebase/Analytics][I-ACS023012] Analytics collection enabled
2020-11-19 17:58:51.139362+0300 Demo [813:165306] 6.34.0 - [Firebase/Analytics][I-ACS023220] Analytics screen reporting is enabled. Call +[FIRAnalytics logEventWithName:FIREventScreenView parameters:] to log a screen view event. To disable automatic screen reporting, set the flag FirebaseAutomaticScreenReportingEnabled to NO (boolean) in the Info.plist
2020-11-19 17:58:51.850205+0300 Demo [813:165012] [AXRuntimeCommon] This class 'SwiftUI.AccessibilityNode' is not a known serializable element and returning it as an accessibility element may lead to crashes
Also Firebase it self shows that I had some reads in stats.
I stuck and don't understand what important part I didn't do in order to start implementing Firebase use. Will appreciate any help or suggestion.!
I used several methods to initiate Firebase in SwiftUI.
This one:
import SwiftUI
import Firebase
#main
struct DemoApp: App {
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL(perform: { url in
Auth.auth().canHandle(url)
})
}
}
Also tried with DelegateAdaptor:
#main
struct DemoApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
appDelegate
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
print("Application is starting up. ApplicationDelegate didFinishLaunchingWithOptions.")
return true
}
}
To test writing to:
func letE() {
let db = Firestore.firestore()
db.collection("wine").addDocument(data: ["Cool" : 2017])
}
And to fetch an array
private var db = Firestore.firestore()
func fetchData() {
db.collection("collection").document("document")
.addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let data = document.data()!["array name"] else {
print("Document data was empty.")
return
}
print(data)
}
And like this:
func getData(){
db.collection("skills").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
}
}
Hello, Peter! I was trying to come to solving this problem from different angels but still no luck.
Basically I am trying to perform wright operation
It goes like this
#main
struct DemoApp: App {
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL(perform: { url in
Auth.auth().canHandle(url)
})
}
}
}
func addData() {
//
let db = Firestore.firestore()
//
db.collection("wine").addDocument(data: ["Test":100])
}
Then i press Build.
From my understanding that already should create collection "Wine" with autogenerated Document with contains field "Test" with value "100"
But once again nothing happens on the firebase side.
The Debug console
2021-01-26 16:22:21.907902+0300 Demo [27616:342494] 6.34.0 - [Firebase/Analytics][I-ACS023007] Analytics v.60900000 started
2021-01-26 16:22:21.908067+0300 Demo [27616:342494] 6.34.0 - [Firebase/Analytics][I-ACS023008] To enable debug logging set the following application argument: -FIRAnalyticsDebugEnabled (see https://help.apple.com/xcode/mac/8.0/#/dev3ec8a1cb4)
2021-01-26 16:22:21.934626+0300 Demo [27616:342498] 6.34.0 - [GoogleUtilities/AppDelegateSwizzler][I-SWZ001014] App Delegate does not conform to UIApplicationDelegate protocol.
2021-01-26 16:22:21.979114+0300 Demo [27616:342490] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed
2021-01-26 16:22:21.994921+0300 Demo [27616:342490] 6.34.0 - [Firebase/Analytics][I-ACS800023] No pending snapshot to activate. SDK name: app_measurement
2021-01-26 16:22:22.000217+0300 Demo [27616:342490] 6.34.0 - [Firebase/Analytics][I-ACS023012] Analytics collection enabled
2021-01-26 16:22:22.000724+0300 Demo [27616:342490] 6.34.0 - [Firebase/Analytics][I-ACS023220] Analytics screen reporting is enabled. Call +[FIRAnalytics logEventWithName:FIREventScreenView parameters:] to log a screen view event. To disable automatic screen reporting, set the flag FirebaseAutomaticScreenReportingEnabled to NO (boolean) in the Info.plist
2021-01-26 16:22:22.032550+0300 Demo [27616:342197] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x60000341ce60 'BIB_Trailing_CB_Leading' H:[_UIModernBarButton:0x7fb0e7d16480]-(6)-[_UIModernBarButton:0x7fb0e7d0a710'Skills'] (active)>",
"<NSLayoutConstraint:0x60000341ceb0 'CB_Trailing_Trailing' _UIModernBarButton:0x7fb0e7d0a710'Skills'.trailing <= _UIButtonBarButton:0x7fb0e7d10cb0.trailing (active)>",
"<NSLayoutConstraint:0x60000341dc20 'UINav_static_button_horiz_position' _UIModernBarButton:0x7fb0e7d16480.leading == UILayoutGuide:0x600002e54ee0'UIViewLayoutMarginsGuide'.leading (active)>",
"<NSLayoutConstraint:0x60000341dc70 'UINavItemContentGuide-leading' H:[_UIButtonBarButton:0x7fb0e7d10cb0]-(0)-[UILayoutGuide:0x600002e54e00'UINavigationBarItemContentLayoutGuide'] (active)>",
"<NSLayoutConstraint:0x60000347eda0 'UINavItemContentGuide-trailing' UILayoutGuide:0x600002e54e00'UINavigationBarItemContentLayoutGuide'.trailing == _UINavigationBarContentView:0x7fb0e7e0f7e0.trailing (active)>",
"<NSLayoutConstraint:0x60000341e3f0 'UIView-Encapsulated-Layout-Width' _UINavigationBarContentView:0x7fb0e7e0f7e0.width == 0 (active)>",
"<NSLayoutConstraint:0x60000347e9e0 'UIView-leftMargin-guide-constraint' H:|-(0)-[UILayoutGuide:0x600002e54ee0'UIViewLayoutMarginsGuide'](LTR) (active, names: '|':_UINavigationBarContentView:0x7fb0e7e0f7e0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60000341ce60 'BIB_Trailing_CB_Leading' H:[_UIModernBarButton:0x7fb0e7d16480]-(6)-[_UIModernBarButton:0x7fb0e7d0a710'Skills'] (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
The changed Debug Console outcome presented is because I've setup authorised access and it does work. I can create user and then perform login with crated login and password. All users do show up on the Firebase console.
I'm very new to React and Firebase. I'm trying to display the object newData as mentioned in the code. I've pushed in a value but later it shows the error in the screen attached.
I'm creating a menu app so I want to add menu into the firebase and also in the mean time return the current menu as well. How could I return from the firebase? How can I update or delete the data already present in the object in firebase? Is there any way to access an object that I had uploaded before? Suppose I push a menu with dishes and price and then how could I delete it or update it. (each specific dish)
Image of the database is attached.
componentWillMount(){
/* Create reference to messages in Firebase Database */
let messagesRef = fire.database().ref('vendor/'+this.state.Day+'/'+this.state.Vendor+'/dishname').orderByKey().limitToLast(100);
messagesRef.on('child_added', snapshot => {
/* Update React state when message is added at Firebase Database */
let message1 = { text: snapshot.val(), id: snapshot.key };
this.setState({ messages: [message1].concat(this.state.messages) });
})
}
addMessage(e){
let messagesR = fire.database().ref('vendor/'+this.state.Day+'/'+this.state.Vendor+'/dishname').orderByKey().limitToLast(100);
messagesR.on('child_added', snapshot => {
/* Update React state when message is added at Firebase Database */
let message1 = { text: snapshot.val(), id: snapshot.key };
this.setState({ messages: [message1].concat(this.state.messages) });
})
var newData={
Type: this.inputE3.value,
Dish: this.inputEl.value,
Price : this.inputE2.value
}
fire.database().ref('vendor/'+this.state.Day+'/'+this.state.Vendor+'/dishname').push(newData);
this.inputEl.value = '';
this.inputE2.value = '';
this.inputE3.value = '';
}
I receive an array of URLs from JSON request
This my code
if let slideShow = self.json["graphql"]["shortcode_media"]["edge_sidecar_to_children"]["edges"][].array {
for allMedia in slideShow {
let medias = allMedia["node"]["display_resources"][2]["src"].stringValue
print("All Media \(medias)")
}
}
I want to show the user a preview of every image, so I need each of these URLs individually. How I can get a single URL from this array?
You just need to create var array of strings, then append a new url string to it inside for loop:
var medias = [String]()
if let slideShow = self.json["graphql"]["shortcode_media"]["edge_sidecar_to_children"]["edges"][].array {
for allMedia in slideShow {
medias.append(allMedia["node"]["display_resources"][2]["src"].stringValue)
}
print("All Media \(medias)")
}
I can create a new calendar and save it with the following function:
func createCalendarForUser() {
let sourcesInEventStore = self.eventStore.sources
//works but doesn't persist
let subscribedSourceIndex = sourcesInEventStore.index {$0.title == "Subscribed Calendars"}
if let subscribedSourceIndex = subscribedSourceIndex {
let userCalendar = EKCalendar(for: .event, eventStore: self.eventStore)
userCalendar.title = "newCalendar"
userCalendar.source = sourcesInEventStore[subscribedSourceIndex]
do {
try self.eventStore.saveCalendar(userCalendar, commit: true)
print("calendar creation successful")
} catch {
print("cal \(userCalendar.source.title) failed : \(error)")
}
}
}
This functions great while the app is open and running. I can save events to them, i can see them in my local calendar, and life is good. However, once the app is terminated and goes into the background the calendars disappear, along with any events created in them. I've tried saving the calendar to different sources other then the Subscribed Calendars source but when i do that the calendars wont even save in the first place. Heres one of the attempts at using the local source:
func createCalendarForUser() {
let sourcesInEventStore = self.eventStore.sources
//never saves calendar
let localSourceIndex = sourcesInEventStore.index {$0.sourceType == .local}
if let localSourceIndex = localSourceIndex {
let userCalendar = EKCalendar(for: .event, eventStore: self.eventStore)
userCalendar.title = "newCalendar"
userCalendar.source = sourcesInEventStore[localSourceIndex]
do {
try self.eventStore.saveCalendar(userCalendar, commit: true)
print("creating new calendar successful")
} catch {
print("creating new calendar failed : \(error)")
}
}
}
I also tried this method :
func createCalendarForUser() {
let sourcesInEventStore = self.eventStore.sources
//doesnt work
let userCalendar = EKCalendar(for: .event, eventStore: self.eventStore)
userCalendar.title = "newCalendar"
userCalendar.source = sourcesInEventStore.filter{
(source: EKSource) -> Bool in
source.sourceType.rawValue == EKSourceType.local.rawValue
}.first!
do {
try self.eventStore.saveCalendar(userCalendar, commit: true)
print("creating new calendar succesful")
} catch {
print("creating new calendar failed : \(error)")
}
}
As metioned here as mentioned here https://www.andrewcbancroft.com/2015/06/17/creating-calendars-with-event-kit-and-swift/
Has anyone else come across this problem?
The blog post you link do does two things when it saves the calendar, it uses the saveCalendar(, commit) method to save the calendar to the event store, and then also saves the identifier for the calendar to user defaults so that it can be retrieved at a later time:
NSUserDefaults.standardUserDefaults().setObject(newCalendar.calendarIdentifier, forKey: "EventTrackerPrimaryCalendar")
You're doing the first, but not the second step, so your calendars will be persisting in the store, but you're not keeping the information needed to retrieve them in the future.