Using Realm with MPMediaQuery - swift

I want to build an Audiobookplayer which can set Bookmarks. Loading the Audiobooks from my Library with MPMediaQuery works fine, but when I take an audiobook off through iTunes, it stays in my realmfile.
I would like realm to delete the entry automatically when the playlist is updated through iTunes, but I can't seem to figure out how.
Here is my code.
class Books: Object {
dynamic var artistName: String?
dynamic var albumTitle: String?
dynamic var artwork: NSData?
dynamic var albumUrl: String?
dynamic var persistentID: String?
let parts = List<BookParts>()
override static func primaryKey() -> String? {
return "persistentID"
}
override class func indexedProperties() -> [String] {
return ["albumTitle"]
}
convenience init(artistName: String, albumTitle: String, albumUrl: String) {
self.init()
self.artistName = artistName
self.albumTitle = albumTitle
self.albumUrl = albumUrl
}
class BookQuery {
let realm = try! Realm()
var bookItems = Array<Books>()
var partItems = Array<BookParts>()
func getBooks() {
let query: MPMediaQuery = MPMediaQuery.audiobooks()
query.groupingType = .album
let collection: [MPMediaItemCollection] = query.collections!
try! realm.write {
for allbooks in collection {
let item = allbooks.representativeItem
let book = Books()
let id = item?.value(forProperty: MPMediaItemPropertyAlbumPersistentID) as! Int
book.artistName = item?.artist
book.albumTitle = item?.albumTitle
book.albumUrl = item?.assetURL?.absoluteString
book.artwork = Helper.getArtwork(item?.artwork) as NSData?
book.persistentID = id.stringValue
realm.add(book, update: true)
guard realm.object(ofType: Books.self, forPrimaryKey: "persistentID") != nil else {
continue
}
bookItems.append(book)
}
}
}
}
I calling the MediaQuery in "viewDidLoad" in my LibraryViewController.
I am pretty new to coding and are trying to solve this for a while.
Thanks for any help.

The high level thing you'll need to do is to have a way to detect when the iTunes playlist is updated and then delete the removed items' corresponding objects from the Realm.
A general approach to this is to get all the "persistent ID"s currently in the Realm at the start of the for loop, put those in an array, remove each ID it sees from the array, then delete objects with the persistent ID in the array that's left, since those weren't in the collection.

Related

Updating collection in Firebase returns error "found nil while unwrapping optional value"?

I'm making this app where the idea is that you create a profile, add your dogs, and then update a timer on them (when they last ate, took a walk, etc). I'm having some issues with Firebase though. I managed to have the user add dogs to their account, but now that I'm trying to update some values on a certain dog the app crashes with a "Unexpectedly found nil while unwrapping an Optional value" which seems to be due to Firebase. My Database contains the user, their dogs and a collection of the dogs values, such as firstTimer. When I try to update this value with the setData() method it just keeps crashing and nothing shows in my database. i've also tried to update values individually but to no avail. Please tell me if I'm going about this the wrong way and if there's some other approach to try, thanks!
import Foundation
import Firebase
import UIKit
//DogViewController
class MyDogViewController: UIViewController {
var db: Firestore!
var auth: Auth!
var storage: Storage!
var thisDog: DogEntry?
var dogRef: DocumentReference!
override func viewDidLoad() {
thisDog?.firstTimer = (formattedDate)
if let dog = thisDog?.toAny() {
print("Let")
//THE PROGRAM PRINTS LET
dogRef.setData(dog)
//BUT CRASHES HERE
}
else {
print("Error")
}
}
}
}
//Dog Modal Class
class DogEntry {
var name: String
var image: String
var firstTimer: String
var secondTimer: String
var walking: Bool = false
var walkArray: [String]
var id: String = ""
init(name: String, image: String, firstTimer: String, secondTimer: String, walking: Bool, walkArray: [String]) {
self.name = name
self.image = image
self.firstTimer = firstTimer
self.secondTimer = secondTimer
self.walking = walking
self.walkArray = walkArray
}
init(snapshot: QueryDocumentSnapshot) {
let snapshotValue = snapshot.data() as [String : Any]
name = snapshotValue["name"] as! String
image = snapshotValue["image"] as! String
firstTimer = snapshotValue["firstTimer"] as! String
secondTimer = snapshotValue["secondTimer"] as! String
walking = snapshotValue["walking"] as! Bool
walkArray = snapshotValue["walkArray"] as! [String]
id = snapshot.documentID
}
func toAny() -> [String: Any] {
return ["name": name, "image": image, "firstTimer": firstTimer, "secondTimer": secondTimer, "walking": walking, "walkArray": walkArray]
}
}
Your dogRef is an implicitly unwrapped optional. You need to give it a value before you call it.

How To Save Only One Instance Of Class In Realm

So instead of using user defualts I want to persist some settings using Realm.
I've created a class for the settings
import Foundation
import RealmSwift
class NutritionSettings: Object {
#objc dynamic var calories: Int = 0
#objc dynamic var proteins: Int = 0
#objc dynamic var carbohydrates: Int = 0
#objc dynamic var fats: Int = 0
}
But in my view controller I don't know how to save just one instance of it
I've tried
let realm = try! Realm()
let settings = NutritionSettings()
do {
try realm.write{
settings.calories = cals!
settings.carbohydrates = carbs!
settings.fats = fats!
settings.proteins = proteins!
}
} catch {
print("error saving settings")
}
Since I know doing realm.add would just add another NutritionSettings object which is not what I want. I was unable to clarify anything using the documentation. Any help would be appreciated thanks.
I faced a similar issue in my project when I tried to save a user session object. If you want to save a unique object, override the primaryKey() class method and set the unique key for it.
#objcMembers class NutritionSettings: Object {
static let uniqueKey: String = "NutritionSettings"
dynamic var uniqueKey: String = NutritionSettings.uniqueKey
dynamic var calories: Int = 0
override class func primaryKey() -> String? {
return "uniqueKey"
}
}
Then to receive the object just use the unique key.
// Saving
let settings = NutritionSettings()
settings.calories = 100
do {
let realm = try Realm()
try realm.write {
realm.add(settings, update: .modified)
}
} catch {
// Error handling
}
// Reading
var settings: NutritionSettings?
do {
let realm = try Realm()
let key = NutritionSettings.uniqueKey
settings = realm.object(ofType: NutritionSettings.self, forPrimaryKey: key)
} catch {
// Error handling
}
if let settings = settings {
// Do stuff
}
Hope it will help somebody.
If you look at the example realm provides https://realm.io/docs/swift/latest you can see that in order to only save one object you still have to do an add. Once you have added the object to the database you can fetch that object and do a write that modifies the internal properties
let realm = try! Realm()
let settings = NutritionSettings()
settings.id = 1
do {
try realm.write{
realm.add(settings)
}
} catch {
print("error saving settings")
}
Next you can fetch and modify that single instance that you saved
let realm = try! Realm()
let settings = realm.objects(NutritionSettings.self).filter("id == 1").first
do {
try realm.write{
settings.calories = cals!
settings.carbohydrates = carbs!
settings.fats = fats!
settings.proteins = proteins!
}
} catch {
print("error saving settings")
}

query Object from Realm List

enter image description herei'm trying to query object from realm
class MessageRealm: Object {
dynamic var fromId = String()
dynamic var messageID = String()
dynamic var textDownloadded = String()
override class func primaryKey() -> String? {
return "messageID"
}
}
class UsersRealm: Object {
dynamic var sender = String()
let msgs = List<MessageRealm>()
override class func primaryKey() -> String? {
return "sender"
}
}
i have two class one for messages and the other for users, every users have a list of messages and i need to query thats message based on (UserRealm.sender)
This is the realm DB
I solve the issue by this way if anyone face the same
var messageIndex: Results<MessageRealm>!
let realm = try! Realm()
let mssagesRealm = realm.objects(UsersRealm.self).filter("sender = %#", userTitleName)
for sub in mssagesRealm {
messageIndex = sub.msgs.sorted(byKeyPath: "timeStamp")
}

create Realm DB for each user in Chat App

this one for sending message and save it to realm db
var messageIndex = try! Realm().objects(MessageRealm.self).sorted(byKeyPath: "timeStamp")
func didPressSend(text: String) {
if self.inputContinerView.inputTextField.text! != "" {
let messageDB = MessageRealm()
let realm = try! Realm()
let userRealm = UsersRealm()
messageDB.textDownloadded = text
messageDB.fromId = user!.fromId
messageDB.timeStamp = Date()
print(messageDB)
try! realm.write ({
print(realm.configuration.fileURL)
userRealm.msgs.append(messageDB)
//realm.create(MessageRealm.self, value: ["textDownloadded": text, "fromId": user!.fromId, "timeStamp": Date()])
})
if let userTitleName = user?.toId {
print(userTitleName)
OneMessage.sendMessage(text, thread: "AAAWatree", to: userTitleName, isPhoto: false, isVideo: false, isVoice: false, isLocation: false, timeStamp: date, completionHandler: { (stream, message) in
DispatchQueue.main.async {
OneMessage.sharedInstance.deleteCoreDataMessage()
}
self.inputContinerView.inputTextField.text! = ""
})
}
}
}
This for when recieving message im trying to save user (send id )
let realm = try! Realm()
userData.sender = sender
userData.toId = toUser
print(userData.sender)
print(userData.toId)
try! realm.write ({
realm.add(userData, update: true)
})
this my Realm Object Class
class MessageRealm: Object {
dynamic var textDownloadded = String()
dynamic var imageDownloadded = NSData()
dynamic var videoDownloadded = String()
dynamic var voiceDownloadded = String()
dynamic var fromId = String()
dynamic var timeStamp = Date()
dynamic var messageId = NSUUID().uuidString
let userSelect = List<UsersRealm>()
override class func primaryKey() -> String? {
return "messageId"
}
}
class UsersRealm: Object {
dynamic var sender = String()
dynamic var fromId = String()
dynamic var toId = String()
dynamic var lastMessage = String()
dynamic var timeStamp = Date()
dynamic var profileImage = NSData()
let msgs = List<MessageRealm>()
override class func primaryKey() -> String {
return "sender"
}
}
sending and reciving is ok and its save to realm db but all any user send message i recived in one user i want to seprate for every user have his sending and recive database i miss something here but i dont know i try to search nothing its long question but i cant figure out the soluation
and sorry for my week english
Thank you
If I understood your case correctly you're using a single realm url for all users that's why all your clients have the same data. You should probably create a separate realm for the conversation and share it between the users who participate in that chat. Please learn more about sharing realms in our docs at https://realm.io/docs/swift/latest/#access-control.

Variable not being saved outside closure

I am using Parse for a database.
Problem: I am querying the database and saving an object, but it does not seem to be saving outside the query function. I think it is because I need to refresh a variable or something but I have no idea how to do that.
Relevant code:
class AddClassViewController: UIViewController {
var classroom = Classroom()
var checkClassList: [Classroom] = []
#IBAction func enrollClassButton(sender: AnyObject) {
self.classroom.classCode = classCodeText.text.uppercaseString
self.classroom.year = yearText.text
self.classroom.professor = professorNameText.text
ClassListParseQueryHelper.checkClass({ (result: [AnyObject]?,error: NSError?) -> Void in
self.checkClassList = result as? [Classroom] ?? []
//count is 1 inside here
println(self.checkClassList.count)
}, classCode: self.classroom.classCode!)
//count is 0 out here
println(self.checkClassList.count)
}
//this gets class with matching classcode
static func checkClass(completionBlock: PFArrayResultBlock, classCode: String){
let query = PFQuery(className: "Classroom")
query.whereKey("ClassCode", equalTo: classCode)
query.findObjectsInBackgroundWithBlock(completionBlock)
}
This is how I solved this:
class ClassListParseQueryHelper{
//this gets class with matching classcode
static func checkClass(classCode: String) -> [PFObject]{
let query = PFQuery(className: "Classroom")
query.whereKey("ClassCode", equalTo: classCode)
var test = query.findObjects() as! [PFObject]
return test
}
self.checkClassList = ClassListParseQueryHelper.checkClass(self.classroom.classCode!) as! [Classroom]