RealmSwift update object in different threads(closures) - swift3

Kindly help with Real objects adding/updating
I'd like store and update User class.
User class consist of Client class,
Client class consist of Avatar property and Rooms List.
Issue is that I'm facing error "The Realm is already in a write transaction" because my Client class avatar property and rooms list are fetched and pushed to Realm in different closures at the same time.
func fetchRooms() {
roomsDelegate?.contactRooms(entityID: entityID,
success: {rooms in
self.addRooms(rooms: rooms)
},
fail: { error in
print (error)
})
}
func addRooms(rooms: [VMRoom]?) {
if let r = rooms {
do{
try realm?.write {
realm?.add(r, update: true)
self.rooms.append(objectsIn: r)
} }
catch let e {
print(e.localizedDescription)
}
}
}
func getAvatarURL() {
do{
try realm?.write {
avatarURL = avatarDelegate?.contactAvatarURL(eExtention: eExtention)
} }
catch let e {
print(e.localizedDescription)
}
}

Like Realm is saying, you've got an error in your app's logic if you're trying to open up two write transactions in one thread. I'd recommend you review your logic to see if you can make it more streamlined.
But in any case, to fix your current code, one way to mitigate this would be to check you're not already in a write transaction when you're setting the avatar URL.
func getAvatarURL() {
let inWriteTransaction = realm?.isInWriteTransaction
do {
if !inWriteTransaction {
realm?.beginWrite()
}
avatarURL = avatarDelegate?.contactAvatarURL(eExtention: eExtention)
if !inWriteTransaction {
try realm.commitWrite()
}
catch let e {
print(e.localizedDescription)
}
}

Related

Non-overridable members may not be used in setup / verification expressions

I'm trying to do some unittesting on a method that is in a FreshMVVM view model (so no interface).
I want to parse two properties with values as well.
I think I found the way to parse the properties. But I get the following exception while running the tests :
Non-overridable members (here: Search ViewModel.ExecuteSearch Command) may not be used in setup / verification expressions.
The method is set public and so are the properties. I can not change them to virtual because then I get an error in my method.
here is my code:
Viewmodel:
public async void ExecuteSearchCommand()
{
ProductionOrders.Clear();
ObservableCollection<ProductionOrder> allProductionorders = await GetDetailedProductionOrders();
if (SelectedSearch == null || Input== null) {
await Application.Current.MainPage.DisplayAlert("woeps", "please make your selection", "OK");
}
else
{
if (SelectedSearch == "Material")
{
foreach (var productionOrder in allProductionorders)
{
if (productionOrder.MaterialNumber == Input)
{
ProductionOrders.Add(productionOrder);
}
}
}
else
{
foreach (var productionOrder in allProductionorders)
{
if (productionOrder.OrderNumber == int.Parse(Input))
{
ProductionOrders.Add(productionOrder);
}
}
}
if (productionOrders.Count == 0)
{
await Application.Current.MainPage.DisplayAlert("woeps", "No data found for this selection", "OK");
}
}
unit test:
[Fact]
public void ExecuteSearchCommand_WitCorrectData_ListProductionOrders()
{
//Arrange
var testMaterial=testMaterials[0];
var testProductionOrder = testProductionOrders[0];
var mockVm = new Mock<SearchViewModel>();
//act
mockVm.Setup(vm => vm.ExecuteSearchCommand()).Equals(testProductionOrder);
mockVm.SetupProperty(se => se.SelectedSearch,"Production Order") ;
mockVm.SetupProperty(ip => ip.Input, "100001");
Assert.NotNull(mockVm);
}
I also tried this:
[Fact]
public void ExecuteSearchCommand_WitCorrectData_ListProductionOrders()
{
//Arrange
var testMaterial=testMaterials[0];
var testProductionOrder = testProductionOrders[0];
var mockVm = new SearchViewModel { SelectedSearch = "Production Order", Input="100001", ProductionOrders=new ObservableCollection<ProductionOrder>() };
mockVm.ExecuteSearchCommand();
//act
Assert.NotNull(mockVm);
}
But then I get an error in the GetDetailedProductionorders method used in the executesearchcommand()
I don't get this error when running the program (not the unit test)
Could someone give me a hint in the right direction?
Thx!
Sarah
From the second unit test you tried, when you create instance of SearchViewModel, there is no initialize of _productionOrderService.
if _productionOrderService is created in SearchViewModel it might not be initialized due to lack of their dependencies.
you have to provide _productionOrderService to the SearchViewModel by
make it public and set it when create SearchViewModel
make it private and pass it through constructor when create SearchViewModel
then you can mock _productionOrderService and setup GetListAllAsync() in unit test

how likely is it to miss a solidity event?

Hi i am making a finance application that depends on Events from blockchain,
Basically i update my database based on Events i receive using web3js, and when user asks i sign with private key for the contract to be able to give user Money.
My only concern is can i depend on events? like can there be a case where i miss events?
here is my code for doing so :
const contract = new web3.eth.Contract(abi, contract_address)
const stale_keccak256 = "0x507ac39eb33610191cd8fd54286e91c5cc464c262861643be3978f5a9f18ab02";
const unStake_keccak256 = "0x4ac743692c9ced0a3f0052fb9917c0856b6b12671016afe41b649643a89b1ad5";
const getReward_keccak256 = "0x25c30c62c42b51e4f667b70ef60f1f683c376f6ace28312ed45a40665e01af37";
let userRepository: Repository<UserData> = connection.getRepository(UserData);
let globalRepository: Repository<GlobalStakingInfo> = connection.getRepository(GlobalStakingInfo);
let userStakingRepository: Repository<UserStakingInfo> = connection.getRepository(UserStakingInfo);
let transactionRepository: Repository<Transaction> = connection.getRepository(Transaction);
const topics = []
web3.eth.subscribe('logs', {
address: contract_address, topics: topics
},
function (error: Error, result: Log) {
if (error) console.log(error)
}).on("data", async function (log: Log) {
let response: Boolean = false;
try {
response = await SaveTransaction(rpc_url, log.address, log.transactionHash, transactionRepository)
} catch (e) {
}
if (response) {
try {
let global_instance: GlobalStakingInfo | null = await globalRepository.findOne({where: {id: 1}})
if (!global_instance) {
global_instance = new GlobalStakingInfo()
global_instance.id = 1;
global_instance = await globalRepository.save(global_instance);
}
if (log.topics[0] === stale_keccak256) {
await onStake(web3, log, contract, userRepository, globalRepository, userStakingRepository, global_instance);
} else if (log.topics[0] === unStake_keccak256) {
await onUnStake(web3, log, contract, userStakingRepository, userRepository, globalRepository, global_instance)
} else if (log.topics[0] === getReward_keccak256) {
await onGetReward(web3, log, userRepository)
}
} catch (e) {
console.log("I MADE A BOBO", e)
}
}
}
)
The Code works and everything, i am just concerned if i could maybe miss a event? cause finance is related and people will lose money if missing event is a thing.
Please advise
You can increase redundancy by adding more instances of the listener connected to other nodes.
And also by polling past logs - again recommended to use a separate node.
Having multiple instances doing practically the same thing will result in multiplication of incoming data, so don't forget to store only unique logs. This might be a bit tricky, because theoretically one transaction ID can produce the same log twice (e.g. through a multicall), so the unique key could be a combination of a transaction ID as well as the log index (unique per block).

Adonis.js - Seeding Users and Profiles throwing DB error (Postgres)

I am trying to write a seed file for users and profiles with a one to one relationship and currently getting an "error: relation 'user_profiles' does not exist". From digging around, it seems like Adonis will assume this as a pivot table in the case of a many to many relationship. What I (think or intend to) have is a one to one relationship between users and profiles. Thanks in advance! Newbie to SQL and Adonis. As a side note, the user persists to the db, but there is no corresponding profile.
// My User Schema
class UserSchema extends Schema {
up () {
this.create('users', (table) => {
table.increments('id')
table.string('username', 80).notNullable().unique()
table.string('email', 254).notNullable().unique()
table.string('password', 60).notNullable()
// table.integer('profile_id').unsigned().references('id').inTable('userprofiles')
table.timestamps()
})
}
down () {
this.drop('users')
}
}
// My Profile Schema
class UserprofileSchema extends Schema {
up () {
this.create('userprofiles', (table) => {
table.increments()
table.string('first_name')
table.string('last_name')
// table.integer('user_id')
// .unsigned().references('id').inTable('users')
table.integer('user_id')
.unsigned()
.index('user_id')
table.foreign('user_id')
.references('users.id')
table.timestamps()
})
}
down () {
this.drop('userprofiles')
}
}
My User model includes the following relationship definition:
profile () {
return this.hasOne('App/Models/UserProfile')
}
// Seed script
class UserSeeder {
async run () {
try {
const user = await Factory.model('App/Models/User').create()
const userProfile = await Factory.model('App/Models/UserProfile').make()
userProfile.user_id = user.id
await user.profile().save(userProfile)
} catch (e) {
console.log('Error From Seeder: ', e);
}
}
}
Error code '42P01' and can post whole body if needed. Thanks!
On your Model userProfile, set table name as follows.
class User extends Model {
static get table () {
return 'userprofiles'
}
}

How to Use Core Data Models with In-Memory Store Type Using Prepopulated SQLite File?

I am following an outdated tutorial from the Ray Wenderlich team that walks through the repopulation of a Core Data-backed application by using a Command Line Tool application.
I have successfully prepopulated the intended entities, verified by performing an NSFetchRequest.
Now, I want to use the same prepopulated data in my unit tests to verify that my interactions with CoreData are happening correctly. I tried setting up my mocked CoreDataStack subclass to use an in-memory store, but when I attempt to verify that I have the prepopulated data for use in my unit tests, I am getting a count of 0.
The class responsible for interacting with CoreData in my application's target, named CoreDataStack, follows:
/// The object that is responsible for managing interactions with Core Data.
internal class CoreDataStack {
// MARK: - Properties
/// The name of the `NSManagedObjectModel` object used for storing information with Core Data.
private let modelName: String
/// The `NSManagedObjectContext` object that is associated with the main queue.
internal lazy var mainContext: NSManagedObjectContext = {
return self.storeContainer.viewContext
}()
/// The `NSPersistentContainer` object that encapsulates the application's Core Data stack.
internal lazy var storeContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: self.modelName)
let directory = NSPersistentContainer.defaultDirectoryURL()
let storeURL = directory.appendingPathComponent("\(self.modelName).sqlite")
if !FileManager.default.fileExists(atPath: (storeURL.path)) {
guard let populatedURL = Bundle.main.url(forResource: self.modelName, withExtension: "sqlite") else {
fatalError("Invalid populated .sqlite file URL")
}
do {
try FileManager.default.copyItem(at: populatedURL, to: storeURL)
} catch {
fatalError("Error: \(error)")
}
}
let description = NSPersistentStoreDescription()
description.url = storeURL
container.persistentStoreDescriptions = [description]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Error: \(error)")
}
})
return container
}()
// MARK: - Initialization
/// Returns an instance of `CoreDataStack`.
/// - parameter modelName: The name of the `NSManagedObjectModel` object used for storing information with Core Data.
internal init(modelName: String) {
self.modelName = modelName
}
/// Attempts to save items to Core Data by committing changes to `NSManagedObject`s in a `NSManagedObjectContext`.
/// - parameter context: The `NSManagedObjectContext` of which changes should be committed.
internal func saveContext(_ context: NSManagedObjectContext) {
context.perform {
do {
try context.save()
} catch let error as NSError {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
}
}
The subclass of CoreDataStack, MockCoreDataStack, used for testing follows:
internal class MockCoreDataStack: CoreDataStack {
// MARK: - Initialization
convenience init() {
self.init(modelName: "Currency")
}
override init(modelName: String) {
super.init(modelName: modelName)
let container = NSPersistentContainer(name: modelName)
let directory = NSPersistentContainer.defaultDirectoryURL()
let storeURL = directory.appendingPathComponent("\(modelName).sqlite")
if !FileManager.default.fileExists(atPath: (storeURL.path)) {
guard let populatedURL = Bundle(for: type(of: self)).url(forResource: modelName, withExtension: "sqlite") else {
fatalError("Invalid populated .sqlite file URL")
}
do {
try FileManager.default.copyItem(at: populatedURL, to: storeURL)
} catch {
fatalError("Error: \(error)")
}
}
let description = NSPersistentStoreDescription()
description.url = storeURL
description.type = NSInMemoryStoreType
container.persistentStoreDescriptions = [description]
container.loadPersistentStores { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
self.storeContainer = container
}
}
The resulting count of my fetch request is 0 in my unit tests target. I expect to return a count consisting of the number of prepopulated objects, just as I get when I return the count in my application's target.
What am I doing incorrectly that is causing me to not return the expected result?
The in memory store doesn't use the store URL. It just creates an empty store in memory.
As an alternative to the in memory store, you could possibly create a parent NSManagedObjectContext between the persistent store and the contexts you actually use. (I'm not sure how that would play with NSPersistentContainer, however.)
Then you can just rollback() the parent context when you want to reset back to your initial state.

Can't create object with existing primary key value

I'm in my first steps of using Realm Mobile Database, and I would like to know if there is a way of handling the error caused by trying to add an object with the same primaryKey as one previously inserted, causing the following Can't create object with existing primary key value.
Here are my snippets:
class CategoryRLM: Object {
dynamic var name: String = ""
dynamic var desc: String = ""
override static func primaryKey() -> String? {
return "name"
}
}
static func addCategory(category: CategoryRLM) -> Bool {
let realm = try! Realm()
do {
try realm.write {
realm.add(category)
}
return true
}
catch let error {
print(error)
return false
}
}
Using the previous function:
if !CategoryRLM.addCategory(category: newCategory) {
// There was an error while adding the object
}
The thing is that the error doesn't get handled by the do-catch.
Attempting to add an object with a primary key that already exists is classed as programmer error (that is, misuse of the API), and as such it is not possible to handle this error at runtime.
In your case you should instead check if an object exists with that primary key prior to attempting to add the category to the Realm:
static func addCategory(category: CategoryRLM) -> Bool {
let realm = try! Realm()
if let existingCategory = realm.object(ofType: CategoryRLM.self, forPrimaryKey: category.name) {
return false
}
try realm.write! {
realm.add(category)
}
return true
}