Swift 3: Return from initializer error - swift

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.

Related

Added property to struct which in Swift - invalidates existing objects

I am new to Swift, but have some basic experience with Objective-C programming, and Swift seems much simpler.
However, I can't quite understand the struct thing. I followed a tutorial on how to use Firebase Realtime Database, and this tutorial were using a model to store the data.
But when I modified the struct with additional properties, the previously saved entries in the database is not showing up. I think it's because the model doesn't recognize the object in the database because it has different properties, but how can I make a property optional? So that old entries in the database with different structure (missing properties) are still valid and showing up?
Here is the model. The new property added is all the references to the description.
import Foundation
import Firebase
struct InsuranceItem {
let ref: DatabaseReference?
let key: String
let name: String
let timestamp: Int
let itemValue: Int
let description: String?
let userId: String?
init(name: String, timestamp: Int, itemValue: Int = 0, description: String = "", userId: String, key: String = "") {
self.ref = nil
self.key = key
self.name = name
self.timestamp = Int(Date().timeIntervalSince1970)
self.itemValue = itemValue
self.description = description
self.userId = userId
}
init?(snapshot: DataSnapshot) {
guard
let value = snapshot.value as? [String: AnyObject],
let name = value["name"] as? String,
let timestamp = value["timestamp"] as? Int,
let itemValue = value["itemValue"] as? Int,
let description = value["description"] as? String,
let userId = value["userId"] as? String else { return nil }
self.ref = snapshot.ref
self.key = snapshot.key
self.name = name
self.timestamp = timestamp
self.itemValue = itemValue
self.description = description
self.userId = userId
}
func toAnyObject() -> Any {
return [
"name": name,
"timestamp": timestamp,
"itemValue": itemValue,
"description": description!,
"userId": userId!
]
}
}
The problematic bit is your failable init, init?(snapshot: DataSnapshot). You fail the init even if an Optional property is missing, which is incorrect. You should only include the non-Optional properties in your guard statement, all others should simply be assigned with the optional casted value.
init?(snapshot: DataSnapshot) {
guard
let value = snapshot.value as? [String: Any],
let name = value["name"] as? String,
let timestamp = value["timestamp"] as? Int,
let itemValue = value["itemValue"] as? Int else { return nil }
self.ref = snapshot.ref
self.key = snapshot.key
self.name = name
self.timestamp = timestamp
self.itemValue = itemValue
// Optional properties
let description = value["description"] as? String
let userId = value["userId"] as? String
self.description = description
self.userId = userId
}
Unrelated to your question, but your toAnyObject function is unsafe, since you are force-unwrapping Optional values. Simply keep them as Optionals without any unwrapping and add as Any to silence the warning for implicit coersion.
func toAnyObject() -> Any {
return [
"name": name,
"timestamp": timestamp,
"itemValue": itemValue,
"description": description as Any,
"userId": userId as Any
]
}

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.

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"
}
}

Firebase Database post error : Cannot invoke initializer for type 'Post' with an argument list of type

I am trying to Post something to my Firebase Database but the post needs the uid so i know from who the post is. I keep getting this error : `Cannot invoke initializer for type 'Post' with an argument list of type '(username: String!, postId: String, postText: String!, postGame: String!, postDate: (NSNumber), postType: String, uid: String!)' But I dont know how i can fix this. Can Someone please help me! Thanks!
import UIKit
import FirebaseAuth
import FirebaseStorage
import FirebaseDatabase
class AddPostViewController: UIViewController,UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var textView: UITextView!
#IBOutlet weak var GameText: UITextField!
var currentUser: User2!
var dataBaseRef: DatabaseReference! {
return Database.database().reference()
}
var storageRef: Storage {
return Storage.storage()
}
func loadUserInfo(){
let userRef = dataBaseRef.child("users/\(Auth.auth().currentUser!.uid)")
userRef.observe(.value, with: { (snapshot) in
self.currentUser = User2(snapshot: snapshot)
}) { (error) in
print(error.localizedDescription)
}
}
#objc func savePost(){
var text: String!
var Game: String!
if let postText = textView.text {
text = postText
}
if let postGame = GameText.text {
Game = postGame
}
let newPost = Post(username: self.currentUser.username, postId: NSUUID().uuidString, postText: text, postGame: Game, postDate: (NSDate().timeIntervalSince1970 as NSNumber), postType: "TEXT", uid: self.currentUser.uid)
let postRef = self.dataBaseRef.child("posts").childByAutoId()
postRef.setValue(newPost.toAnyObject(), withCompletionBlock: { (error, ref) in
if error == nil {
self.navigationController!.popToRootViewController(animated: true)
}else {
print(error!.localizedDescription)
}
})
}
}
Here is my Post model:
import Foundation
import UIKit
import FirebaseDatabase
import FirebaseAuth
import Firebase
struct Post {
var username: String!
var Game: String!
var Console: String!
var ExtraInfo: String!
var uid: String!
var postId: String!
var postType: String!
var postText: String!
var postDate: NSNumber!
var ref: DatabaseReference!
var key: String?
init(snapshot: DataSnapshot){
self.ref = snapshot.ref
self.key = snapshot.key
self.username = (snapshot.value! as! NSDictionary)["username"] as! String
self.Console = (snapshot.value! as! NSDictionary)["Console"] as! String
self.ExtraInfo = (snapshot.value! as! NSDictionary)["ExtraInfo"] as! String
self.Game = (snapshot.value! as! NSDictionary)["Game"] as! String
self.postId = (snapshot.value! as! NSDictionary)["postId"] as! String
self.postType = (snapshot.value! as! NSDictionary)["postType"] as! String
self.postDate = (snapshot.value! as! NSDictionary)["postDate"] as! NSNumber
self.postText = (snapshot.value! as! NSDictionary)["postText"] as! String
self.uid = (snapshot.value! as! NSDictionary)["uid"] as! String
}
init(username: String, postId: String, Game: String, Console: String, ExtraInfo: String, postText: String, postDate: NSNumber, postType: String, uid: String){
self.username = username
self.Game = Game
self.Console = Console
self.ExtraInfo = ExtraInfo
self.postText = postText
self.postType = postType
self.uid = uid
self.postDate = postDate
self.postId = postId
}
func toAnyObject() -> [String: Any] {
return ["username": username, "postId":postId,"Game": Game , "Console": Console,"ExtraInfo": ExtraInfo ,"postType":postType, "postDate":postDate, "postText":postText,"uid": uid]
}
}
The problem is here:
let newPost = Post(username: self.currentUser.username, postId: NSUUID().uuidString,
postText: text, postGame: Game, postDate: (NSDate().timeIntervalSince1970 as NSNumber),
postType: "TEXT", uid: self.currentUser.uid)
You're trying to initialize a Post object, but according to the init you created, it needs to have these components:
init(username: String, postId: String, Game: String, Console: String,
ExtraInfo: String, postText: String, postDate: NSNumber, postType: String,
uid: String)
So instead, for newPost, it should have the correct initializer like:
let newPost = Post(username: self.currentUser.username, postId: NSUUID().uuidString,
Game: Game, Console: /*I don't know what goes here */, ExtraInfo: String,
postText: text, postDate: (NSDate().timeIntervalSince1970 as NSNumber),
postType: "TEXT", uid: self.currentUser.uid)

Missing argument for parameter 'dictionary' in call

This is my NSObject class file that I am using to populate my collectionView cell. I am fetching my data from firebase and populating the collectionViewCell with it. Xcode is giving this error "Missing argument for parameter 'dictionary' in call" I have tried all I can but I could not figure out what is missing. What is causing this error and how can I fix it?
class BusinessCategory: NSObject {
var ref: FIRDatabaseReference!
var name: String?
var logo: String?
var featurebusiness: [SampleBusinesses]?
var type: String?
init(dictionary: [String: Any]) {
self.name = dictionary["BusinessName"] as? String ?? ""
self.logo = dictionary["logo"] as? String ?? ""
}
static func sampleBusinessCategories() -> [BusinessCategory] {
var FinancialInstitutionCatergory = BusinessCategory()
FinancialInstitutionCatergory.name = "Financial Institutions"
var featurebusiness = [SampleBusinesses]()
//logic
FIRDatabase.database().reference().child("BusinessCategories/Banks").observeSingleEvent(of: .childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let financeApp = SampleBusinesses()
financeApp.setValuesForKeys(dictionary)
financeApp.name = dictionary["BusinessName"] as? String
featurebusiness.append(financeApp)
}
FinancialInstitutionCatergory.featurebusiness = featurebusiness
print(snapshot)
}, withCancel: nil)
return [FinancialInstitutionCatergory]
}
}
The error is stating you need to include the dictionary parameter in whatever line has the error; an example might look something like this:
var FinancialInstitutionCatergory = BusinessCategory(dictionary: [String : Any])
It's unclear which line in your code has the error; you'll need to include the parameter somewhere.