I have edited the question to focus on the last error. This is the last error in my code: an Array extension to remove object by value
It seems to be related to this post too:
Array extension to remove object by value
But I am stuck :x
func cell(cell: FriendSearchTableViewCell, didSelectUnfollowUser user: PFUser) {
if var followingUsers = followingUsers {
ParseHelper.removeFollowRelationshipFromUser(PFUser.currentUser()!, toUser: user)
// update local cache
removeObject(user, fromArray: &followingUsers)
self.followingUsers = followingUsers
}
// for the 'removeObject' an error is raised:
Use of unresolved identifier 'removeObject'
The function is calling the framework Foundation through a Array+RemoveObject.swiftfile which states:
import Foundation
// Thanks to Martin R: https://stackoverflow.com/questions/24938948/array-extension-to-remove-object-by-value
extension Array where Element : Equatable {
// Remove first collection element that is equal to the given `object`:
mutating func removeObject(object : Generator.Element) {
if let index = self.indexOf(object) {
self.removeAtIndex(index)
}
}
}
I am not sure my workspace is properly understanding that he needs to refer to this swift file to find the details of the identified removeObject.
PFUser does not conform to Equatable protocol so your extension doesn't apply. But a PFUser is identified through its user name. You can solve your problem with filter, no extension required:
func cell(cell: FriendSearchTableViewCell, didSelectUnfollowUser user: PFUser) {
if var followers = followingUsers {
ParseHelper.removeFollowRelationshipFromUser(PFUser.currentUser()!, toUser: user)
// update local cache
followers = followers.filter { $0.username != user.username }
self.followingUsers = followers
}
}
Related
The Realm documentation for an interface-driven write indicates that you can add a record to a collection like this:
func insertItem() throws {
// Perform an interface-driven write on the main thread:
collection.realm!.beginWrite()
collection.insert(Item(), at: 0)
// And mirror it instantly in the UI
tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
// Making sure the change notification doesn't apply the change a second time
try collection.realm!.commitWrite(withoutNotifying: [token])
}
This seems to imply that the datasource for the table is an Array because there is not an insert method on a Results<Item> collection.
What data type is collection in this situation? It seems like it's an array, but my understanding is that you can't create a Realm notification on an array.
I've also read that it's not a good idea to copy all your Realm objects into an array (for performance reasons) since the results are accessed lazily. But it would seem that interface-driven writes are impossible unless you do. 🤔
Any suggestions on how to properly set this up?
The documentation is a bit vague but the specific answer to your question is that in this case, collection is a List type. See the documentation for Collections.
To drill down a bit, here's an example implementation.
Suppose we have a Person Object and each person has a List property of Dog Objects.
class PersonClass: Object {
#objc dynamic var person_name = ""
let dogs = List<DogClass>()
}
class DogClass: Object {
#objc dynamic var dog_name = ""
#objc dynamic var dog_age = 0
let owners = LinkingObjects(fromType: PersonClass.self, property: "dogs")
}
We want to know when a specific person gets a new dog, update our tableView with that dog info immediately and not receive a Realm notification for the event.
Here's the code to set up an observer for Jean-Luc's dogs.
//a class var for the notification and the List.
var dogListNotificationToken: NotificationToken? = nil
var dogList: List<DogClass>? = nil
func doObserveDogList() {
if let realm = gGetRealm() { //using a singleton Realm for this example
let personResults = realm.objects(PersonClass.self).filter("name == 'Jean-Luc'")
let firstPerson = personResults.first
self.dogList = firstPerson?.dogs
}
if self.dogList == nil {
print("there were no dogs for this person")
return
}
self.dogListNotificationToken = self.dogList!.observe { (changes: RealmCollectionChange) in
switch changes {
case .initial:
print("initial object load complete")
if let results = self.dogList {
for d in results {
print(d.dog_name, d.dog_age)
}
}
break
case .update(_, let deletions, let insertions, let modifications):
print(" handle item delete, insert or mod")
for index in deletions {
print(" deleted object at index: \(index)")
}
for index in insertions {
print(" inserted object at index: \(index)")
}
for index in modifications {
print(" modified object at index: \(index)")
}
break
case .error(let error):
// An error occurred
fatalError("\(error)")
break
}
}
}
and suppose Jean-Luc get gets a new dog so we need to insert that into realm but don't want a notification because we want to handle it 'immediately'.
func insertDog() {
let dogToAdd = DogClass()
dogToAdd.dog_name = "Number 1"
dogToAdd.dog_age = 5
self.dogList?.realm?.beginWrite()
self.dogList?.insert(dogToAdd, at: 0)
//insert into tableView/datasource/whatever
try! self.dogList?.realm!.commitWrite(withoutNotifying: [self.dogListNotificationToken!])
}
The above will result in the Dog 'Number 1' being added to Jean-Luc's dog list with no observe event being triggered upon the insert.
I am using RealmSwift to create a PIN code screen for an app. I have a manager class that has a few functions, including checkForExistingPin() which is intended to be used to check whether a pin exists (as the name suggests).
When I create an instance of the manager class and call the checkForExistingPin() function, it always tells me that there are 4 (It prints: "Optional(4)"), even though I have not created a pin yet.
Can anyone explain why this might be doing this and how I might get the correct output from the code?
Here is the class:
import Foundation
import RealmSwift
class pinCode: Object {
#objc dynamic var pin = ""
}
protocol pinCodeManager {
func checkForExistingPin() -> Bool
func enterNewPin(newPin:String)
func checkPin(pin:String) -> Bool
}
class manager:pinCodeManager {
let realm = try! Realm()
func checkForExistingPin() -> Bool {
let existingCode = realm.objects(pinCode.self).first?.pin
print("\n\nNumber of existing PINs: ", existingCode?.count as Any, "\n\n") // Number of existing PINs: Optional(4)
if existingCode?.count == 0 {
return false
}
else {
return true
}
}
func enterNewPin(newPin:String) {
if checkForExistingPin() {
let oldCode = realm.objects(pinCode.self).first
try! realm.write {
oldCode!.pin = newPin
}
}
let newPinObject = pinCode()
newPinObject.pin = newPin
realm.add(newPinObject)
}
func checkPin(pin:String) -> Bool {
if checkForExistingPin() {
print ("Realm object first: ", realm.objects(pinCode.self).first?.pin as Any)
if pin == realm.objects(pinCode.self).first?.pin {
print ("Pin Correct")
return true
}
else {
print ("Pin Incorrect")
return false
}
}
print ("No existing pin")
return false
}
}
And here is the relevant code snippet of the ViewController:
class InitialViewController: UIViewController {
let myPin = pinCode()
let myManager = manager()
let realm = try! Realm()
#IBAction func NewUserButton(_ sender: Any) {
print("No existing PINs: ", self.myManager.checkForExistingPin())
}
The output is : Number of existing PINs: Optional(4)
You must have created a pinCode object (or multiple of them). "Optional(4) doesn't mean you have created 4 pins. You are counting String. It means that the object you retrieved has a 4 digit pin. If you haven't created any pinCode object, you should get nil. Or if you have created one without assigning a pin, you should get 0.
I recommend your looking at your realm file. You should be able to print out its location this way:
print(Realm.Configuration.defaultConfiguration.fileURL!)
You can then open the file with Realm Studio and verify what is in there.
You have a few things going on here:
Although this is not really in the scope of the question, here's a tip for the future. Your types' names should be capitalized (following CamelCase standard), as per Swift API Design Guidelines. Thus, your pinCodes and manager classes and pinCodeManager protocol should be called PinCode, Manager and PinCodeManager respectively.
Assuming you renamed your types and as other users pointed out, you're not counting instances of PinCode. You're counting the length of the pin member of PinCode class. Refactoring your checkForExistingPin() function:
func checkForExistingPin() -> Bool {
return realm.objects(pinCode.self).count > 0
}
In your enterNewPin(newPin:) function, in the case you already have a PinCode object stored, note that you are actually updating the old PinCode and adding a new one with the same pin. For instance, if you previously have a PinCode object stored with pin=1234. After calling enterNewPin(newPin: "5678") you will have two such objects stored with the pin=5678. You might want to refactor that as well:
func enterNewPin(newPin:String) {
if checkForExistingPin() {
let oldCode = realm.objects(pinCode.self).first
try! realm.write {
oldCode!.pin = newPin
}
} else {
let newPinObject = pinCode()
newPinObject.pin = newPin
try! realm.write {
realm.add(newPinObject)
}
}
}
Before trying to do any debugging in your app. I recommend you first uninstalling and then reinstalling the app wherever you running (simulator or actual device). If things keep behaving weird, that's probably something related with your configuration if you're overriding the default one (i.e. I noticed that you just used try! Realm() for retrieving a Realm, but you might have overridden Realm.Configuration.defaultConfiguration somewhere else).
Hope that helps!
Looking to submit an array to firebase as a list of objects with integers as key names. I know that firebase does not support Arrays directly so was wondering how to do so. I am creating a list of items users add a users cart. so I am approaching it as such:
func addItemtoCart(item: String, completed: #escaping (_ completed: Bool) -> ()) { Firebase_REference_Cart.child(userID).child("itemIDs").updateChildValues(["itemiD": itemID])
completed(true)
}
I understand that this will not work because every time and item is added to the cart it will replace the item in under the "ItemId". I was looking to have something like this
CartITems: {
0: "945495949856956",
1: "9459469486895695"
2: "J888568567857685"
}
If someone could please describe how to do this from A to Z in the most descriptive way possible it would help greatly. I am new to firebase and need a bit of guidance.
First of all create model like:
struct Model {
var id: String
init(id: String) {
self.id = id
}}
Then make an instance of this model in your class: var model = Model(id: "First Id")
Then to push it to Firebase use:
func addInstance(model: Model) -> Completable {
return Completable.create( subscribe: { completable in
let reference = Database.database()
.reference()
.child("Models")
.childByAutoId
var model = model
model.id = reference.key
reference.setValue(model.dictionary)
self.modelVariable.value.append(model)
completable(.completed)
}
else {
completable(.error(NSError(domain: "Sample", code: 403, userInfo: [NSLocalizedDescriptionKey: "Server Error"])))
}
return Disposables.create()
})
Hi i have followed this tutorial: https://www.youtube.com/watch?v=XIQsQ2injLo
This explains how to save and retrieve from the database, but not how to delete. I am wondering how to delete the database node that belongs to the cell that is being deleted. Thanks
Edit. Code updated for Swift 3 and Swift 4.
I'm always using the remove with completion handler:
static let ref = Database.database().reference()
static func remove(child: String) {
let ref = self.ref.child(child)
ref.removeValue { error, _ in
print(error)
}
}
So for example if I want to delete the following value:
I call my function: remove(child: "mixcloudLinks")
If I want to go deeper and delete for example "added":
I need to change the function a little bit.
static func remove(parentA: String, parentB: String, child: String) {
self.ref.child("mixcloudLinks").child(parentA).child(parentB).child(child)
ref.removeValue { error, _ in
print(error)
}
}
Called like:
let parentA = "DDD30E1E-8478-AA4E-FF79-1A2371B70700"
let parentB = "-KSCRJGNPZrTYpjpZIRC"
let child = "added"
remove(parentA: parentA, parentB: parentB, child: child)
This would delete just the key/value "added"
EDIT
In case of autoID, you need to save the autoID into your Dictionary to be able to use it later.
This is for example one of my functions:
func store(item: MyClassObject) {
var item = item.toJson()
let ref = self.ref.child("MyParentFolder").childByAutoId()
item["uuid"] = ref.key // here I'm saving the autoID key into the dictionary to be able to delete this item later
ref.setValue(item) { error, _ in
if let error = error {
print(error)
}
}
}
Because then I'm having the autoID as part of my dictionary and am able to delete it later:
Then I can use it like .child(MyClassObject.uuid).remove... which is then the automatic generated id.
we can store the randomly generated id as the user id in the database and retrieve that to delete
for e.g. :
let key = ref?.childByAutoId().key
let post = ["uid": key,
"name": myName,
"task": myTask]
ref?.child(key!).setValue(post)
in order to delete
setvalue of the id as nil
for e.g. :
var key = [string]()
ref?.child(key[indexPath.row]).setValue(nil)
key.remove(at: indexPath.row)
myArray.remove(at: indexPath.row)
myTable.reloadData()
I have request data which was about 7MB after it has downloaded the json string,means the json string is about 7MB.After it has downloaded,I would like to save the data into realm model object(table) with progress like
(1/7390) to (7390/7390) -> (data which is inserted/total data to be inserted)
I am using Alamofire as HTTPClient at my app.So,how to insert data with progress into my realm object model after it has downloaded from server?Any help cause I am a beginner.
I wont show the data model exactly,so,any example is appreciated.Let say my json string is.
{
{
name : Smith,
age : 23,
address : New York
},
{
name : Adam,
age : 22,
address : Maimi
},
{
name : Johnny,
age : 33,
address : Las Vegas
},
... about 7392 records
}
Supposing you have a label for do this.
Ok.
Supposing you using MVVM pattern too.
Ok.
ViewController has label and "observe"(*) the ViewModel property 'progress'
ViewModel has property 'progress'
class ViewModel: NSObject {
dynamic var progress: Int = 0
func save(object object: Object) {
do {
let realm = try Realm()
try realm.write({ () -> Void in
// Here your operations on DB
self.progress += 1
})
} catch let error as NSError {
ERLog(what: error)
}
}
}
In this way viewController is notified when "progress" change and you can update UI.
Your VC should have a method like this, called by viewDidLoad for instance:
private func setupObservers() {
RACObserve(self.viewModel, keyPath: "progress").subscribeNext { (next: AnyObject!) -> Void in
if let newProgress = next as? Int {
// Here update label
}
}
}
Where RACObserve is a global function:
import Foundation
import ReactiveCocoa
func RACObserve(target: NSObject!, keyPath: String) -> RACSignal {
return target.rac_valuesForKeyPath(keyPath, observer: target)
}
(*) You can use ReactiveCocoa for instance.
Katsumi from Realm here. First, Realm has no way to know the total amount of data. So calculating progress and notifying it should be done in your code.
A total is a count of results array. Store count as a local variable. Then define progress variable to store the number of persisted. progress should be incremented every save an object to the Realm. Then notify the progress.
let count = results.count // Store count of results
if count > 0{
if let users = results.array {
let progress = 0 // Number of persisted
let realm = try! Realm()
try realm.write {
for user in users{
let userList=UserList()
[...]
realm.add(userList,update: true)
progress += 1 // After save, increment progress
notify(progress, total: count)
}
}
}
}
So there are several ways to notify the progress. Here we use NSNotificationCenter for example:
func notify(progress: Int, total: Int) {
NSNotificationCenter
.defaultCenter()
.postNotificationName("ProgressNotification", object: self, userInfo: ["progress": progress, "total": total])
}