I dont know what I am doing wrong. I am trying sync from my MongoDB Realm Cloud to local database for access.
I am following this instruction
https://docs.mongodb.com/realm/ios/sync-data/#ios-sync-data
here is my mongoDB Realm screen shot. It shows the items i want to sync with the partioningKey.
here is my code
override func viewDidLoad() {
super.viewDidLoad()
fetchStoreItems()
}
func fetchStoreItems(){
let user = app.currentUser()
let partitionValue = "store=walmart"
Realm.asyncOpen(configuration: user!.configuration(partitionValue: partitionValue),
callback: { (maybeRealm, error) in
guard error == nil else {
fatalError("Failed to open realm: \(error!)")
}
guard let realm = maybeRealm else {
fatalError("realm is nil!")
}
// realm opened
// All tasks in the realm
let storeItems = maybeRealm!.objects(Item.self)
let tasksThatBeginWithA = storeItems.filter("name beginsWith 'R'")
print("these are the item amount", tasksThatBeginWithA.count)
})
}
Authentication works fine but fetching the data returns empty with this message
Sync: Connection2: Session2: client_reset_config = false, Realm exists = true, async open = false, client reset = false
what am I doing wrong and how can I fix it?
Pretty sure you're accessing the wrong object, but it's a little hard to tell by the question. Items is the highlighted object in the screen shot so it seems that's the one you're after (?)
It looks like your database has two objects
Item
and
items
but your code is accessing the Item one
let storeItems = maybeRealm!.objects(Item.self)
not the items one.
Related
I'm calling a Firestore query that does come back, but I need to ensure completion before moving on with the rest of the code. So I need a completion handler...but for the life of me I can't seem to code it.
// get user info from db
func getUser() async {
self.db.collection("userSetting").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let userTrust = document.data()["userTrust"] as! String
let userGrade = document.data()["userGrade"] as! String
let userDisclaimer = document.data()["userDisclaimer"] as! String
var row = [String]()
row.append(userTrust)
row.append(userGrade)
row.append(userDisclaimer)
self.userArray.append(row)
// set google firebase analytics user info
self.userTrustInfo = userTrust
self.userGradeInfo = userGrade
}
}
}
}
Called by:
internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
db = Firestore.firestore()
Database.database().isPersistenceEnabled = true
Task {
do {
let userInfo = await getUser()
}
} return true }
I used a Task as didFinishLauncingWithOptions is synchronous and not asynchronous
However, the getUser() still isn't completed before didFinishLauncingWithOptions moves on.
I need the data from getUser as the very next step uses the data in the array, and without it I get an 'out of bounds exception' as the array is still empty.
Also tried using dispatch group within the func getUser(). Again with no joy.
Finally tried a completion handler:
func getUser(completion: #escaping (Bool) -> Void) {
self.db.collection("userSetting").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let userTrust = document.data()["userTrust"] as! String
let userGrade = document.data()["userGrade"] as! String
let userDisclaimer = document.data()["userDisclaimer"] as! String
var row = [String]()
row.append(userTrust)
row.append(userGrade)
row.append(userDisclaimer)
self.userArray.append(row)
// set google firebase analytics user info
self.userTrustInfo = userTrust
self.userGradeInfo = userGrade
completion(true)
}
}
}
}
Nothing works. The getUser call isn't completed before the code moves on. Can someone please help. I've searched multiple times, looked at all linked answers but I can not make this work.I'm clearly missing something easy, please help
read this post: Waiting for data to be loaded on app startup.
It explains why you should never wait for data before returning from
function application(_:didFinishLaunchingWithOptions).
To achieve what you need, you could use your first ViewController as a sort of splashscreen (that only shows an image or an activity indicator) and call the function getUser(completion:) in the viewDidLoad() method the ViewController.
Example:
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
MyFirestoreDatabaseManager.shared.getUser() { success in
if success {
//TODO: Navigate to another ViewController
} else {
//TODO: Show an error
}
}
}
}
Where obviously MyFirestoreDatabaseManager.shared is the object on which you defined the getUser(completion:) method.
(In your example, I think that you defined that function in the AppDelegate. In that case, you should mark your getUser(completion:) method and all related variables as static. Then replace MyFirestoreDatabaseManager.shared with AppDelegate).
Not 100% sure what you would like to accomplish as I can't see all your code, but try something similar to this, replacing Objects for what you are trying to return from the documents.
You don't want your user's data spread across multiple documents. With Firebase you pay for every document you have to get. Ideally you want all your user's settings within one firebase document. Then create a UserInfo struct that you can decode to using the library CodeableFirebase or the decoder of your choice.
// Create user struct
struct UserInfo: Codable {
var userId: String
var userTrust: String
var userGrade: String
var userDisclaimer: String
}
// get user info from db and decode using CodableFirebase
func getUser() async throws -> UserInfo {
let doc = try await self.db.collection("users").document("userIdHere")
let userInfo = try FirestoreDecoder().decode(UserInfo.self, from: doc.data())
return UserInfo
}
Then you can do this...
Task {
do {
let userInfo = try await getUser()
let userTrust = userInfo.userTrust
let userGrade = userInfo.userGrade
let userDisclaimer = userInfo.userDisclaimer
}
}
There is an api (https://docs.api.jikan.moe/#section/Information). I get data from it, but I can’t display them in my collection views in any way. The data should come, I checked. I implement filling the collection view cells through the view model ViewController <-> ViewModel and with Network Manager API Manager
The result is just white collectionView - Screen
For the first time I decided to work with Alamofire and apparently I don’t understand something. Please tell me what is the problem. Link to github in case someone needs it.
Updated
The problem might be with asynchronous coding. And i still have no ideas to fix it, cause don't understand the GCD as well. Screen
func fetchRequest(typeRequest: TypeRequest) -> [AnimeModel] {
var animeModels: [AnimeModel] = []
switch typeRequest {
case .name(let name):
let urlString = "https://api.jikan.moe/v4/anime?q=\(name)"
AF.request(urlString).response { response in
guard let data = response.data else { return print("NO DATA FOR - \(name)") }
do {
let json = try JSON(data: data)
let title = json["data"][0]["title_english"].string ?? "Anime"
let imageURL = json["data"][0]["images"]["jpg"]["image_url"].string ?? ""
let image = AnimeModel.downloadImage(stringURL: imageURL)
animeModels.append(AnimeModel(image: image, title: title))
print(".NAME ANIME MODELS - \(animeModels)")
} catch let error {
print(error.localizedDescription)
}
}
}
print("BEFORE RETURN ANIME MODELS - \(animeModels)")
return animeModels // returns empty array and then "animeModel.append()" is applied
}
Sorry about the title, i’m not quite sure how to stay this succinctly. In any case, in my (iOS/Swift but not SwfitUI) app, the user can edit an object (for example Team) over a number of screens. When I first start, I create a team using
// Team is subclass of Realm Object
let team = Team()
let realm = try Realm()
// save copy of team into a property or something in memory
realm.beginWrite()
realm.add(team)
}
and then later in the app, after a number of interactions with the user:
var team = // fetch copy of team from memory
let realm = try Realm()
team.name="bar"
// other updates to "team".
try realm.commitWrite()
This seems to work fine, however I was just wondering if this is “OK” to do since I’m calling beginWrite and commitWrite in different calls to let realm = Realm().
I’m sure that the user has not and will not perform any other realm queries/writes during this flow since if the user “cancels” out of editing the “team”, I call realm.cancelWrite.
Lastly, does the answer change when using a synced realm vs. a local realm?
Thanks!
A more complete (yet still simplified) example of what I’m doing looks like this:
class MyViewController: UIViewController {
private var team: Team?
#IBOutlet weak var nameTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
do {
// setup realm configuration up here somewhere
let realm = try Realm()
realm.beginWrite()
let team = Team()
realm.add(team)
} catch {
print("Error: \(error)")
}
}
#objc func submitTapped(_ sender: Any) {
let name = self.nameTextField.text!.trim()
guard let team = self.team else {
return
}
do {
let realm = try Realm()
self.team.name = name
try realm.commitWrite()
} catch {
print ("Error: \(error)")
}
}
}
I have a simple entity with identifier (constraint) and name fields. In ViewController I try to add data to the database without worrying about duplicates and there really is no duplicate data in the database, but when I try to get records, I get twice as many of them. As I found out, this only happens when I try to write something to the database, and regardless of whether the attempt was successful, the data goes to my NSFetchRequestResult. What is the reason for this behavior?
DB content now:
ViewController:
class ViewController: UIViewController {
let moc = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad() {
super.viewDidLoad()
//if comment this loop I won't get duplicates
for i in 0...5 {
let ent = Entity(context: moc)
ent.identifier = Int16(i)
ent.name = "Username"
try? moc.save() //if the code is not executed for the first time, then the attempt is unsuccessful due to identifier constraint
}
let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Entity")
let fetchedEntities = try? moc.fetch(fetch) as? [Entity]
print(fetchedEntities!.count) // Output: 12 (actually only 6 records in the db)
}
}
Change your code where you create the objects to
for i in 0...5 {
let ent = Entity(context: moc)
ent.identifier = Int16(i)
ent.name = "Username"
}
do {
try moc.save()
} catch {
moc.reset()
}
This way you will remove the faulty (duplicate) objects from the context
I've got 2 models- Cards and Channels - and a Channel owns Cards
class Card : Object {
let channel = LinkingObjects(fromType: Channel.self, property: "cards")
}
class Channel: Object {
let cards = List<Card>()
}
In my UICollectionViewController, I'm using this as my Results:
var results: Results<Card> = {
let realm = try! Realm()
return realm.objects(Card).filter("ANY channel.is_following = true")
.filter("ANY channel.live = true")
.filter("published == true AND retracted == false").sorted("published_at", ascending: false)
}()
and my notification block set in my viewDidLoad:
notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
print("in notif block")
}
I'm trying to make use of the new RealmCollectionChange features, but when I make a write/change to a Channel it isn't reflected in my Card results which should trigger the notification block. It shows the right stuff at first launch / initialization, but doesn't keep up with the changes after that. My pre-RealmCollectionChange code still works fine for me (catch all notifications and manually refresh my results), but wanted to see if anyone has any pointers before I roll back.
Thanks in advance.