Can't update relationship


#1

Hello,

For some reason, the relationship of “user” is not updated and no error is thrown.
The goal is to update it signalling that invitation is accepted.
If I leave just status update - it work just fine

What I’m doing wrong?

enum MemberStatus: String, CaseIterable {
  case invited
  case accepted
  case declined
  
  func displayView() -> String {
    switch self {
    case .invited: return "Invited"
    case .accepted: return "Accepted"
    case .declined: return "Declined"
    }
  }
}

class MemberItem: Object {
  @objc dynamic var uid: Int = UUID().hashValue
  @objc dynamic var email: String?
  @objc dynamic var user: UserItem?
  @objc dynamic var inviter: UserItem?

@objc dynamic private var _memberStatus = MemberStatus.invited.rawValue
  var memberStatus: MemberStatus {
    get { return MemberStatus(rawValue: _memberStatus)! }
    set { _memberStatus = newValue.rawValue }
  }

override class func primaryKey() -> String? {
    return "uid"
  }
}

class UserItem: Object {
  @objc dynamic var uid: Int = UUID().hashValue
  @objc dynamic var owner: String = ""
  @objc dynamic var email: String = ""
}

fileprivate func withRealm<T>(_ operation: String, action: (Realm) throws -> T) -> T? {
    do {
      let realm = try! Realm(configuration: SyncUser.current!.configuration())
      
      return try action(realm)
    } catch let err {
      print("Failed \(operation) realm with error: \(err)")
      return nil
    }
  }


func getMember(uid: Int) -> Observable<MemberItem> {
    let result = withRealm("fetching member by uid") { realm -> Observable<MemberItem> in
      
      let members = realm.objects(MemberItem.self).filter("uid == %@", uid)
      let subscriptionToken = members.subscribe()
      
      return Observable.collection(from: members)
        .flatMapLatest { results -> Observable<MemberItem> in
          return Observable<MemberItem>.create { observer in
            if let member = results.first {
              observer.onNext(member)
            } else {
              print("NO MEMBERS in RESULTS: \(results)")
            }
            
            return Disposables.create {
              subscriptionToken.unsubscribe()
            }
          }
      }
    }
    
    return result ?? .error(MemberServiceError.getMemberFailed)
  }

func getViewer() -> Observable<UserItem> {
    guard let identity = SyncUser.current?.identity else { return .empty() }
    
    let result = withRealm("getting current viewer") { realm -> Observable<UserItem> in
      let users = realm.objects(UserItem.self).filter("owner == %@", identity)
      
      let subscriptionToken = users.subscribe()
      
      return Observable.collection(from: users).flatMap { results -> Observable<UserItem> in
        return Observable<UserItem>.create { observer in
          if let user = results.first {
            observer.onNext(user)
          } else {
            print("NO  USER in RESULTS: \(results)")
          }
          
          return Disposables.create {
            subscriptionToken.unsubscribe()
          }
        }
      }
      }?.share(replay: 1)
    
    return result ?? .empty()
  }

// where update happens in another function
...
func problematicFunctionCALL() -> Observable<MemberItem> {
 let result = withRealm("connect") { realm -> Observable<MemberItem> in
      return Observable.zip(
        self.getMember(uid: memberUID),
        self.getViewer()
      ).flatMapLatest { (member, viewer) -> Observable<MemberItem> in

        do {
          try realm.write {
            member.memberStatus = .accepted
            member.user = viewer // <<< this update is ignored!

            //          if let inviter = member.inviter {
            //            inviter.friends.append(viewer)
            //            viewer.friends.append(inviter)
            //
            //          }
          }
        } catch let err {
          print(err.localizedDescription)
        }


        return .just(member)
      }
    }
    
    return result ?? .error(MemberServiceError.invitationAcceptFailed)
}

#2

There’s a lot of code in the question; some of it appears to be unrelated to the question and it’s difficult to decipher due to some of the naming and function calls. For example; the problematic code calls:

self.getMember(uid: memberUID)

typically when you get something it returns a value like this

let aMember = self.getMember...

which then allows you to work on that particular var

aMember.user = viewer

etc. If that function assigns a value to a class var, I am not seeing that in the code, and there are a number of returns in that function so it’s unclear (to me) how that works.

The other issue is how the properties are assigned. This code would theoretically assign values to the memberStatus and user properties of a Member class

        member.memberStatus = .accepted
        member.user = viewer // <<< this update is ignored!

however, the MemberItem Class doesn’t have both of those properties, perhaps there’s another class that does?

class MemberItem: Object {
  @objc dynamic var uid: Int = UUID().hashValue
  @objc dynamic var email: String?
  @objc dynamic var user: UserItem?
  @objc dynamic var inviter: UserItem?
}

So where do those properties come from as it’s not defined the included code anywhere? If those properties are not part of the Realm Object, they won’t get written to Realm.

Can you shorten the code and clarify what you’re asking? Then we can take a look.


#3

Hello Jay,

first of all - thank you for answer. Yes, it’s not easy code to understand as I use ReactiveX technology to tame the async nature of things. I have updated original code to complete the picture.

The issue is exactly with user property of MemberItem not being propagated to remote Realm on .write with no error or any explanation. But as documents says - all realms in QueryBased mode has a default of all permissions set to everyone

Both member and viewer properties got resolved asynchronously but the resulting callback called only when I have both properties. this is important to link relationships.