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

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.

Related

after update - Unexpectedly found nil while unwrapping an Optional value

After I've updated my project I get this error:
Unexpectedly found nil while unwrapping an Optional value
class Search {
private var _username: String!
private var _userImg: String!
private var _userKey: String!
private var _userRef: DatabaseReference!
var currentUser = KeychainWrapper.standard.string(forKey: "uid")
var username: String {
return _username <- error
}
var userImg: String {
return _userImg
}
var userKey: String{
return _userKey
}
init(username: String, userImg: String) {
_username = username
_userImg = userImg
}
init(userKey: String, postData: Dictionary<String, AnyObject>) {
_userKey = userKey
if let username = postData["username"] as? String {
_username = username
}
if let userImg = postData["userImg"] as? String {
_userImg = userImg
}
_userRef = Database.database().reference().child("messages").child(_userKey)
}
}
It worked fine under Swift 3 and Firebase 3.0.2, but now, where everything is update, it crashes all the time. It's not a duplicate to any other question as it worked all before.
I am not sure I fully understand the question or what exactly is causing the crash (it's probably a missing child node) or what the use case is of the implicitly unwrapped class vars but in response to a comment, here's what I would do in Swift 4, Firebase 4
Leave your Search class as is except change the init to the following (this is shortened to provide context)
init(withSnap: DataSnapshot) {
_userKey = withSnap.key
let dict = withSnap.value as! [String: Any]
_username = dict["username"] as? String ?? "NO USER NAME!"
_userImg = dict["userImg"] as? String ?? "NO IMAGE"
}
and then the Firebase function to get a user (for example) would look like this
let userRef = self.ref.child("users").child("uid_0")
userRef.observeSingleEvent(of: .value, with: { snapshot in
let mySearch = Search(withSnap: snapshot)
print(mySearch.userKey, mySearch.username, mySearch.userImg)
})
You would need to add in the rest of the class code to assign _userRef etc.
The idea here is to provide default values to the required class properties in case one of the Firebase nodes didn't exist and results in nil. i.e. if uid_0 didn't have a Username child node your class would crash (which it is). With the code above, that property would be set to a default value.
And for thoroughness suppose a user node looks like this
users
uid_0: "some string" //the value here is a String, not a Dictionary
that would crash my code. To prevent that, add more error checking in the init
init(withSnap: DataSnapshot) {
_userKey = withSnap.key
if let dict = withSnap.value as? [String: Any] {
_username = dict["username"] as? String ?? "NO USER NAME!"
_userImg = dict["userImg"] as? String ?? "NO IMAGE"
} else {
_username = "No user data"
_userImg = "No user data"
}
}

Instance Member Cannot Be Used On Type - Firebase

I am new to Swift and I am following a tutorial on how to create a social media app with Xcode and Firebase. However, I got this error:
Instance member 'database' cannot be used on type 'DatabaseReference'
Here is my code:
import Foundation
import Firebase
class Post {
private var _username: String!
private var _userImg: String!
private var _postImg: String!
private var _likes: Int!
private var _postKey: String!
private var _postRef: DatabaseReference
var username: String {
return _userImg
}
var postImg: String {
get {
return _postImg
}set {
_postImg = newValue
}
}
var likes: Int {
return _likes
}
var postKey: String {
return _postKey
}
init(imgUrl: String, likes: Int, username: String, userImg: String) {
_likes = likes
_postImg = postImg
_username = username
_userImg = userImg
}
init(postKey: String, postData: Dictionary<String, AnyObject>) {
_postKey = postKey
if let username = postData["username"] as? String {
_username = username
}
if let userImg = postData["userImg"] as? String{
_userImg = userImg
}
if let postImage = postData["imageUrl"] as? String {
_postImg = postImage
}
if let likes = postData["likes"] as? Int {
_likes = likes
}
_postRef = DatabaseReference.database().reference().child("posts")
}
}
I get my error on the third to last line that says:
_postRef = DatabaseReference.database().reference().child("posts")
The database property is an instance type, meaning it must be referenced by an instance of DatabaseReference. Your call to DatabaseReference.database is accessing for a class, or static, type. You need to change your call to an instance of DatabaseReference.
Presumably, you need to initialize an instance of DatabaseReference. I don't know Firebase to know what is required for that, but that will take care of your issue.
Essentially:
let databaseReference = DatabaseReference() // Likely won't work, but some init method here will
_postRef = databaseReference.database()... // Whatever you need here
It sounds like you're looking for either:
_postRef = Database.database().reference("posts")
Or
_postRef = DatabaseReference.root.child("posts")

Using Realm with MPMediaQuery

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.

Swift 3: Return from initializer error

I'm getting and error that reads: Return from initializer without initializing all stored properties.
I'm using swift 3 and xcode 8 also firebase as my backend.
import Foundation
import FirebaseDatabase
struct Post {
var ref: FIRDatabaseReference
var key: String!
var username: String!
var postId: String!
var postText: String!
init(username: String, postId: String, postText: String, key: String = ""){
self.username = username
self.postId = postId
self.postText = postText
}//<--- im getting the error right here
init(snapshot: FIRDataSnapshot) {
let values = snapshot.value as! Dictionary<String,String>
self.username = values["username"]
self.postText = values["postText"]
self.postId = values["postId"]
self.ref = snapshot.ref
self.key = snapshot.key
}
func toAnyObject() -> [String: AnyObject]{
return ["username":username as AnyObject, "postText":postText as AnyObject,"postId":postId as AnyObject]
}
}
Any idea on how i can fix this?
The stored property ref is a non-optional type.
According the rules all non-optional properties must be initialized in each provided initializer.
That means you have to assign an initial value to the property or you make the property optional.

Swift 2.2 singleton

I am new in Swift. I am trying to parse some JSON data from web service and want a singleton class of user.But I got stuck to create the singleton. Here is my code:
import Foundation
class User {
private var success: String
private var userId: String
private var name: String
private var gender: String
private var email: String
private var userObject = [User]()
class var sharedInstane:User {
struct Singleton {
static var onceToken: dispatch_once_t = 0
static var instance:User? = nil
}
dispatch_once(&Singleton.onceToken){
Singleton.instance = User()
}
return Singleton.instance!
}
private init(success: String, userId: String, name: String, gender: String, email: String)
{
self.success = success
self.userId = userId
self.name = name
self.gender = gender
self.email = email
}
convenience init(dictionary: [String:AnyObject]) {
let success = dictionary["success"] as? String
let userId = dictionary["userId"] as? String
let name = dictionary["name"] as? String
let gender = dictionary["gender"] as? String
let email = dictionary["email"] as? String
self.init(success: success!, userId: userId!, name: name!, gender: gender!, email: email!, )
}
func callWebserviceToLoadUserInfo (url:String, param:[String:AnyObject],completeHandler:(Bool?,String) -> ())
{
let connection = ServerConnection()
connection.getJSONDataForWebService(url, params: param) { (response, error) in
// code goes here
var responseDict = response as! [String : AnyObject]
responseDict = responseDict["responseDict"] as! [String : AnyObject]
if responseDict["success"] as! String == "1" {
for dict in responseDict {
let user = User(dictionary: (dict as! [String:AnyObject]))
self.userObject.append(user)
}
print("user : \(self.userObject[0].name)")
}else{
// error goes here
}
}
}
}
Can any one please help me how should I do this code?
The singleton in the single line sample code.
class TheOneAndOnlyKraken {
static let sharedInstance = TheOneAndOnlyKraken()
private init() {} //This prevents others from using the default '()' initializer for this class.
}
For more details.
Using Krakendev's single-line singleton code, cited by Maheshwar, and turning your convenience init into an instance function to be called with User.sharedInstance.initialize(dictionary):
import Foundation
class User {
// Here you declare all your properties
// "private var" and all that jazz
static let sharedInstance = User()
private init() {
// If you have something to do at the initialization stage
// you can add it here, as long as it does not involve
// arbitrary values that you would pass as parameters.
}
func initialize(dictionary: [String:AnyObject]) {
// Transfer the values of the dictionary to each `self.property`.
// Be careful while using `as?` as you may have to deal with
// optionals. No need to call `self.init` at the end, because
// this is now a regular `func`.
}
// Add the rest of your stuff here
}
One note about how you were working inside of that convenience initializer: if you do property = SomeClass.someMethod().someProperty as? SomeType, then property will be of type SomeType?, or Optional(SomeType). According to The Swift Programming Language,
The conditional form, as?, returns an optional value of the type you are trying to downcast to.
While User was not instantiated at least one time sharedInstance will return nil. After the first successful instantiation of the User, sharedInstance starts return it and that's became impossible to instantiate another one User as singleton pattern requires it. Consider this:
class User {
private static var sharedUser: User?
class var sharedInstance: User? {
return sharedUser
}
private init(success: String, userId: String, name: String, gender: String, email: String)
{
//User initialization code here
User.sharedUser = self
}
convenience init?(dictionary: [String:AnyObject]) {
guard User.sharedUser == nil else {
return nil
}
//dictionary parsing code is here
self.init(success: success!, userId: userId!, name: name!, gender: gender!, email: email!)
}
}
Client's code:
User.sharedUser
//return nil
let dict: [String:AnyObject] = ["success": "success", "userId":"userId", "name":"name", "gender":"gender","email":"email"]
User(dictionary: dict)
//creates User
User.sharedUser
//returns just created user
User(dictionary: dict)
//return nil
You should think about making this two classes, so that User is your model class and then create a manager to handle all the users (which seems to be your goal).
So in User remove the sharedInstane part and create a second singleton class, e.g. called UserManager, with the standard way to create a singleton in Swift. Then you can keep the way you're creating your user and in the end just assign it to the singleton:
class UserManager {
static let sharedInstance = UserManager()
var users = [User]()
}
// in your code:
...
for dict in responseDict {
let user = User(dictionary: (dict as! [String:AnyObject]))
UserManager.sharedInstance.users.append(user)
}
...