Getting a nil error with UITextView - swift

I am creating an app that will have several segments of it that will connect to parse, so I thought I would try to create a custom Class for the parse functions.
This address book tab is the first attempt.
I hit a snag with something that I thought would be so simple, but after 10 hours of research, I am turning here.
Here is the ViewController
var addressUUID = NSUUID().UUIDString
class AddViewController : UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var addressImage : UIImageView!
#IBOutlet weak var nameField : UITextField!
#IBOutlet weak var lastNameField : UITextField!
#IBOutlet weak var phoneField : UITextField!
#IBOutlet weak var emailField : UITextField!
#IBOutlet weak var addressCityField : UITextField!
#IBOutlet weak var addressCountryField : UITextField!
#IBOutlet weak var nameFieldLabel : UILabel!
#IBOutlet weak var lastNameFieldLabel : UILabel!
#IBOutlet weak var phoneFieldLabel : UILabel!
#IBOutlet weak var emailFieldLabel : UILabel!
#IBOutlet weak var addressCityFieldLabel : UILabel!
#IBOutlet weak var addressCountryFieldLabel : UILabel!
#IBOutlet weak var doneButton: UIButton!
#IBOutlet weak var scrollView: UIScrollView!
var scrollViewHeight : CGFloat = 0
var person : Person?
var parse : ParseData?
// creating frame for keyboard to force scroll view up
var keyboard = CGRect()
#IBAction func addButtonPressed(sender : UIButton) {
//NSLog("Button pressed")
print ("\(nameField.text)")
parse = ParseData.init(firstName: nameField.text!)
// Saves data to Parse class, regardless of if new or updated record
do {
try parse!.setFirstName(nameField.text!)
try parse!.setLastName(lastNameField.text!)
try parse!.setPhone(phoneField.text!)
try parse!.setEmail(emailField.text!)
try parse!.setAddressCity(addressCityField.text!)
try parse!.setAddressCountry(addressCountryField.text!)
try parse!.setAddressImage(addressImage.image!)
try parse!.setUUID(addressUUID)
} catch let error as PersonValidationError {
var errorMsg = ""
switch(error) {
case .InvalidFirstName:
errorMsg = "Invalid first name"
case .InvalidAddressCity:
errorMsg = "Invalid City"
case .InvalidEmail:
errorMsg = "Invalid email address"
case .InvalidPhone:
errorMsg = "Invalid phone number"
case .InvalidAddressImage:
errorMsg = "Invalid Image"
case .InvalidAddressCountry:
errorMsg = "Invalid Country"
}
let alert = UIAlertController(title: "Error", message: errorMsg, preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Okay", style: .Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
} catch {
}
if person == nil
{
parse!.saveAddressToParse()
} else {
parse!.updateAddressToParse()
}
As noted, the error is the first line of "try"
The strange thing is the data sent to the Person class works fine.
Also, when I had the Parse functions on this VC, it worked (albeit with a modified code)
Here is the class ParseData
import Foundation
import Parse
enum ParseValidationError : ErrorType {
case InvalidFirstName
case InvalidAddressCity
case InvalidPhone
case InvalidEmail
case InvalidAddressCountry
case InvalidAddressImage
}
// class ParseData : PFObject, PFSubclassing
class ParseData : PFObject, PFSubclassing
{
private(set) var firstName : String?
private(set) var lastName : String?
private(set) var addressCity : String?
private(set) var addressCountry : String?
private(set) var phone : String?
private(set) var email : String?
private(set) var uuid : String?
private(set) var addressImageFile : UIImage?
var person : Person?
init?(firstName fn: String) {
super.init()
do {
try setFirstName(fn)
} catch {
return nil
}
}
static func parseClassName() -> String {
return "ParseData"
}
func saveAddressToParse () {
print ("saveToParse function begins")
let savedAddressObject = PFObject(className: "addressBook")
savedAddressObject["firstName"] = self.firstName!
savedAddressObject["lastName"] = self.lastName!
savedAddressObject["phone"] = self.phone!
savedAddressObject["email"] = self.email!
savedAddressObject["addressCity"] = self.addressCity!
savedAddressObject["addressCountry"] = self.addressCountry!
savedAddressObject["username"] = PFUser.currentUser()!.username
savedAddressObject["uuid"] = addressUUID
savedAddressObject["entryFrom"] = "Divelog New"
let addressBookImageData = UIImageJPEGRepresentation(self.addressImageFile!, 0.5)
let addressBookImageFile = PFFile(name: "addressBookImage.jpg", data: addressBookImageData!)
savedAddressObject ["addressBookImage"] = addressBookImageFile
savedAddressObject.pinInBackground()
savedAddressObject.saveEventually()
}
func updateAddressToParse () {
print ("updateToParse function begins")
let updateAddressQuery = PFQuery(className: "addressBook")
updateAddressQuery.whereKey("uuid", equalTo: person!.uuid!)
updateAddressQuery.getFirstObjectInBackgroundWithBlock {(objects: PFObject?, error: NSError?) -> Void in
if error == nil {
if let updateAddressObject = objects {
updateAddressObject.setValue(self.firstName!, forKey: "firstName")
updateAddressObject.setValue(self.lastName!, forKey: "lastName")
updateAddressObject.setValue(self.phone!, forKey: "phone")
updateAddressObject.setValue(self.email!, forKey: "email")
updateAddressObject.setValue(self.addressCity!, forKey: "addressCity")
updateAddressObject.setValue(self.addressCountry!, forKey: "addressCountry")
updateAddressObject.setValue("Divelog Update", forKey: "entryFrom")
let addressBookImageData = UIImageJPEGRepresentation(self.addressImageFile!, 0.5)
let addressBookImageFile = PFFile(name: "addressImage.jpg", data: addressBookImageData!)
updateAddressObject.setValue(addressBookImageFile!, forKey: "addressBookImage")
updateAddressObject.pinInBackground()
updateAddressObject.saveEventually()
}
}
}
}
func setFirstName(fn : String) throws {
firstName = fn
}
func setLastName(ln : String) throws {
lastName = ln
}
func setPhone (ph : String) throws {
phone = ph
}
func setEmail (em : String) throws {
email = em
}
func setAddressCity(adc : String) throws {
addressCity = adc
}
func setAddressCountry(ad : String) throws {
addressCountry = ad
}
func setAddressImage(ai : UIImage) throws {
addressImageFile = ai
}
func setUUID(ui : String) throws {
uuid = ui
}
}
Needed to add to the AppDelete:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
ParseData.registerSubclass()
Parse.enableLocalDatastore()
The above is the corrected answer and it works fine.

I cant see, where you create an instance of ParseData. I see, that you declare it with
var parse : ParseData?
and then in addButtonPressed you use it. But in between, where is the code that defines it? Something like:
parse = ParseData()

Related

How to pass API image from Table View into another View Controller using didselectrowat

I am a newbie in Swift and I am trying to build an app in which I retrieve plant images and information from this api "https://rapidapi.com/mnai01/api/house-plants2".
I managed to implement a table view in which I display the name and image of each plant in the api, and when I click on any cell in the table view I displayed that certain plant's information in a new view controller.
My problem is that no matter what I tried I couldn't also display the image of that plant in that view controller and I don't know what to do to make it work.
It is also worth to mention that the links for the images are of this format:
img: "http://www.tropicopia.com/house-plant/thumbnails/5556.jpg"
This is the class of the view controller where the image and information should be displayed:
import UIKit
import SDWebImage
class PlantDetailsViewController: UIViewController {
// image view for the plant
#IBOutlet weak var plantImage: UIImageView!
// labels for the plant information
#IBOutlet weak var commonNameLabel: UILabel!
#IBOutlet weak var latinNameLabel: UILabel!
#IBOutlet weak var otherNamesLabel: UILabel!
#IBOutlet weak var categoryLabel: UILabel!
#IBOutlet weak var useLabel: UILabel!
#IBOutlet weak var styleLabel: UILabel!
#IBOutlet weak var familyLabel: UILabel!
#IBOutlet weak var bloomSeasonLabel: UILabel!
#IBOutlet weak var wateringLabel: UILabel!
#IBOutlet weak var idealLightLabel: UILabel!
#IBOutlet weak var growthLabel: UILabel!
#IBOutlet weak var climatLabel: UILabel!
#IBOutlet weak var diseaseLabel: UILabel!
#IBOutlet weak var insectsLabel: UILabel!
#IBOutlet weak var leafColourLabel: UILabel!
#IBOutlet weak var bloomsColourLabel: UILabel!
#IBOutlet weak var availabilityLabel: UILabel!
#IBOutlet weak var bearingLabel: UILabel!
#IBOutlet weak var appealLabel: UILabel!
var plants: Plant?
var strCommonName = ""
var strLatinName = ""
var strOtherNames = ""
var strCategory = ""
var strUse = ""
var strStyle = ""
var strFamily = ""
var strBloomSeason = ""
var strWatering = ""
var strIdealLight = ""
var strGrowth = ""
var strClimat = ""
var strDisease = ""
var strInsects = ""
var strLeafColour = ""
var strBloomsColour = ""
var strAvailability = ""
var strBearing = ""
var strAppeal = ""
override func viewDidLoad() {
super.viewDidLoad()
commonNameLabel.text = strCommonName
latinNameLabel.text = strLatinName
otherNamesLabel.text = strOtherNames
categoryLabel.text = strCategory
useLabel.text = strUse
styleLabel.text = strStyle
familyLabel.text = strFamily
bloomSeasonLabel.text = strBloomSeason
wateringLabel.text = strWatering
idealLightLabel.text = strIdealLight
growthLabel.text = strGrowth
climatLabel.text = strClimat
diseaseLabel.text = strDisease
insectsLabel.text = strInsects
leafColourLabel.text = strLeafColour
bloomsColourLabel.text = strBloomsColour
availabilityLabel.text = strAvailability
bearingLabel.text = strBearing
appealLabel.text = strAppeal
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
This is is the didSelectRowAt function for the table view:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let detail:PlantDetailsViewController = self.storyboard?.instantiateViewController(withIdentifier: "showDetails") as! PlantDetailsViewController
detail.strCommonName = plants[indexPath.row].common_name?.first ?? "N/A"
detail.strLatinName = plants[indexPath.row].latin_name ?? "N/A"
detail.strOtherNames = plants[indexPath.row].other_names ?? "N/A"
detail.strCategory = plants[indexPath.row].categories ?? "N/A"
detail.strUse = plants[indexPath.row].use?.first ?? "N/A"
detail.strStyle = plants[indexPath.row].style ?? "N/A"
detail.strFamily = plants[indexPath.row].family ?? "N/A"
detail.strBloomSeason = plants[indexPath.row].blooming_season ?? "N/A"
detail.strWatering = plants[indexPath.row].watering ?? "N/A"
detail.strIdealLight = plants[indexPath.row].light_ideal ?? "N/A"
detail.strGrowth = plants[indexPath.row].growth ?? "N/A"
detail.strClimat = plants[indexPath.row].climat ?? "N/A"
detail.strDisease = plants[indexPath.row].disease ?? "N/A"
detail.strInsects = plants[indexPath.row].insects?.first ?? "N/A"
detail.strLeafColour = plants[indexPath.row].color_of_leaf?.first ?? "N/A"
detail.strBloomsColour = plants[indexPath.row].color_of_blooms ?? "N/A"
detail.strAvailability = plants[indexPath.row].availability ?? "N/A"
detail.strBearing = plants[indexPath.row].bearing ?? "N/A"
detail.strAppeal = plants[indexPath.row].appeal ?? "N/A"
self.navigationController?.pushViewController(detail, animated: true)
}
In the Manager folder I created class called "APICaller" where I fetch the data from the API. This is the function that does that:
func getAllPlants (completion: #escaping (Result<[Plant], Error>) -> Void) {
guard let url = URL(string: "\(Constants.baseURL)/all/?rapidapi-key=\(Constants.API_KEY)") else {return}
let task = URLSession.shared.dataTask(with: URLRequest(url: url)) { data, _, error in
guard let data = data, error == nil else {return}
do {
let results = try JSONDecoder().decode([Plant].self, from: data)
completion(.success(results))
} catch {
completion(.failure(APIError.failedTogetData))
}
}
task.resume()
}
And finally this is the Plant struct model:
struct Plant: Codable {
let appeal: String?
let availability: String?
let bearing: String?
let blooming_season: String?
let categories: String?
let climat: String?
let color_of_blooms: String?
let color_of_leaf: [String]?
let common_name: [String]?
let disease: String?
let family: String?
let growth: String?
let insects: [String]?
let latin_name: String?
let light_ideal: String?
let other_names: String?
let style: String?
let use: [String]?
let watering: String?
let id: String?
let img: String?
let url: String?
private enum CodingKeys: String, CodingKey {
case appeal = "Appeal"
case availability = "Availability"
case bearing = "Bearing"
case blooming_season = "Blooming season"
case categories = "Categories"
case climat = "Climat"
case color_of_blooms = "Color of blooms"
case color_of_leaf = "Color of leaf"
case common_name = "Common name"
case disease = "Disease"
case family = "Family"
case growth = "Growth"
case insects = "Insects"
case latin_name = "Latin name"
case light_ideal = "Light ideal"
case other_names = "Other names"
case style = "Style"
case use = "Use"
case watering = "Watering"
case id = "id"
case img = "Img"
case url = "Url"
}
}
I think the problem is that each image is a string which contains a link, and to be able to display it in the table view cells I used SDWebImage. The question is how do I do that to display the image in the detail view controller? Thank you for your time. Any help or piece of advice is greatly appreciated :)
UPDATE- I tried to display it like this :
I wrote this in the viewdidload function in the detail view controller:
var selectedImage: String?
if let imageToLoad = selectedImage {
plantImage.image = UIImage(named: imageToLoad)
}
and then I added this line in the didselectrowat function:
detail.selectedImage = plants[indexPath.row].img
It still doesn't work and I don't know what I am doing wrong
Swift 5.5, Xcode 14.2
plantImage.image = UIImage(named: imageToLoad) is wrong, named is to local images, when you want get images from one API, like "http://www.tropicopia.com/house-plant/thumbnails/5556.jpg", you need use :
Github Example: https://github.com/MaatheusGois/answer-75361391
let url = URL(string: image.url)
func downloadImage(from url: URL) {
print("Download Started")
getData(from: url) { data, response, error in
guard let data = data, error == nil else { return }
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
// always update the UI from the main thread
DispatchQueue.main.async { [weak self] in
self?.plantImage.image = UIImage(data: data)
}
}
}
func getData(from url: URL, completion: #escaping (Data?, URLResponse?, Error?) -> Void) {
URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}
IMPORTANT (Common error)
https://developer.apple.com/forums/thread/119977

I am trying to add a UILabel exactly like I already have but keep getting this error

I am trying to add a label to my calculator where it shows the tip amount but I keep getting
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
I just want it to display the tip amount as well. I copied it exact for the other UILabel.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var PriceTxt: UITextField!
#IBOutlet weak var Tip: UITextField!
#IBOutlet weak var totalFinal: UILabel!
#IBOutlet weak var TipAmount: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
totalFinal.text = ""
TipAmount.text = ""
}
#IBAction func Calcualte(_ sender: Any) {
if PriceTxt.text! == "" || Tip.text! == ""
{
totalFinal.text = "Input the Numbers"
TipAmount.text = ""
}
else {
let price = Double(PriceTxt.text!)!
let tipPer = Double(Tip.text!)!
let TipMult = price * (tipPer/100)
let TipFinal = Double((round(100*TipMult)/100) + price)
totalFinal.text = "$\(TipFinal)"
TipAmount.text = "$\(TipMult)"
}
}
}
It will show you the problem in your code.. please always try to avoid force cast
class ViewController: UIViewController {
#IBOutlet weak var PriceTxt: UITextField!
#IBOutlet weak var Tip: UITextField!
#IBOutlet weak var totalFinal: UILabel!
#IBOutlet weak var TipAmount: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
totalFinal.text = ""
TipAmount.text = ""
}
#IBAction func Calcualte(_ sender: Any) {
if let getPrice = PriceTxt.text , let getTip = Tip.text
{
if getPrice.isEmpty || getTip.isEmpty {
totalFinal.text = "Input the Numbers"
TipAmount.text = ""
}
else {
let price = Double(getPrice)!
let tipPer = Double(getTip)!
let TipMult = price * (tipPer/100)
let TipFinal = Double((round(100*TipMult)/100) + price)
totalFinal.text = "$\(TipFinal)"
TipAmount.text = "$\(TipMult)"
}
} else {
print("either PriceTxt or Tip is nil")
}
}
}
Try using ? instead of !
! force unwraps and gives you a fatal error if there is no value (nil). ? only unwraps if a value is present.
Also, why unwrap the text here at all?
Use
PriceTxt.text == "" || Tip.text == "" without the ! or ?.

Swift 4: Expression implicitly coerced from '[String : String?]' to '[AnyHashable : Any]'

Why I getting an error message in the line that says:
usersReference.updateChildValues(values, withCompletionBlock: { (err, ref)
The error message says:
Expression implicitly coerced from '[String : String?]' to '[AnyHashable : Any]'
What changes could I make to my code to prevent the error message?
Here is all of my code from the view controller:
import UIKit
import Firebase
import FirebaseAuth
class RegisterViewController: UIViewController {
private var ref: DatabaseReference! // референс к БД
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
#IBOutlet weak var firstNameField: UITextField!
#IBOutlet weak var lastNameField: UITextField!
#IBOutlet weak var cityField: UITextField!
#IBOutlet weak var telNumField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference() // инициализация БД
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func nextButtonPressed(_ sender: Any) {
Auth.auth().createUser(withEmail: self.emailField.text!, password: self.passwordField.text!) { (user, error) in
if error != nil {
print(error!)
self.showAlert(title: "Error!", msg: "Invalid information", actions: nil)
return
}
print("Registration succesfull")
guard let uid = user?.uid else { //доступ к ID пользователя
return
}
self.ref = Database.database().reference() // инициализация БД
let usersReference = self.ref.child("users").child(uid)
let values = ["firstname": self.firstNameField.text, "lastname": self.lastNameField.text, "email": self.emailField.text, "city": self.cityField.text, "telnumber": self.telNumField.text]
usersReference.updateChildValues(values, withCompletionBlock: { (err, ref) in
if let err = err {
print(err)
return
}
print("Saved user successfully into Firebase db")
})
}
}
What changes could I make to my code to prevent the error message?
this isn't error where compiler won't let you run your code without fixing it, this is just warning. But message of this warning is important since if you didn't fix it, your string would be saved like this:
Optional("Text from text field")
So, in your case problem is, that you're passing optional property text of type String? as Any which shouldn't be optional.
You can silence this warning and fix your code by force-unwrapping text properties of your text fields (it's safe to force-unwrap it here because, regarding to docs, this property is never nil)
let values = ["firstname": self.firstNameField.text!, "lastname": self.lastNameField.text!, "email": self.emailField.text!, "city": self.cityField.text!, "telnumber": self.telNumField.text!]

Converting [String] into String

Im trying to pass a username from my TableViewCell to another form via didSelectRowAt. The usernames in the TableViewCell are generated via an Array
import UIKit
import Firebase
import FirebaseDatabase
var userNames = [String] ()
var planType = [String] ()
var planDesc = [String] ()
var planDate = [String] ()
//Index contains item in array that we want to display
var myIndex = 0;
class RequestsTableViewController: UITableViewController
{
#IBOutlet weak var lblUsernameX: UILabel!
var userNameX = ""
var dbReference: FIRDatabaseReference!
var usersReference : FIRDatabaseReference!
override func viewDidLoad()
{
super.viewDidLoad()
lblUsernameX.text = userNameX
//reference to database
self.dbReference = FIRDatabase.database().reference()
//reference to users in database
self.usersReference = dbReference.child("MH_PlanRequest")//.queryOrdered(byChild: "username")
//data comes into a snapshot object
self.usersReference.observe(.childAdded, with: { (snapshot) in
//var usersSnapshotArray = [FIRDataSnapshot]()
//from the snapshot get the entry as key-value (KV)pair
//use a swift native Dictionary object to hold the KV pair
let snapshotValue = snapshot.value as! Dictionary<String, String>
//use the keys to get the values
let userName = snapshotValue["username"]! as String
let date = snapshotValue["date"]! as String
let type = snapshotValue["plantype"]! as String
let desc = snapshotValue["plandesc"]! as String
//print(userName)
//print(date)
//print(planType)
userNames.append(userName)
planDate.append(date)
planType.append(type)
planDesc.append(desc)
self.tableView.reloadData()
})
//print("Reloading Data....")
//tableView.reloadData()
//print("Data Reloaded....")
//tableView.reloadData()
}
And the array is taken from my Firebase Data Storage
and im getting this error when I click on the cell and it goes to the other form.
Error:
Cannot convert value of type '[String]' to expected argument type 'String'
The other form contains labels and based on the username label they would load in the ViewDidLoad and the data is taken from the User from the Firebase.
import UIKit
import Firebase
import FirebaseDatabase
class ManageRequestViewController: UIViewController
{
#IBOutlet weak var lblUsernameX: UILabel!
var userNameX = ""
#IBOutlet weak var lblDate: UILabel!
#IBOutlet weak var lblUsername: UILabel!
#IBOutlet weak var lblPlanType: UILabel!
#IBOutlet weak var lblActivityLevel: UILabel!
#IBOutlet weak var lblWeight: UILabel!
#IBOutlet weak var lblAge: UILabel!
#IBOutlet weak var lblHeight: UILabel!
#IBOutlet weak var lblCalorie: UILabel!
#IBOutlet weak var txtDesc: UITextView!
#IBOutlet weak var txtDietPlan: UITextView!
var dbActivityLevel = ""
var dbWeight = ""
var dbAge = ""
var dbHeight = ""
var dbReference: FIRDatabaseReference!
var usersReference : FIRDatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
lblUsernameX.text = userNameX
lblUsername.text = userNames[myIndex]
//lblDesc.text = petDesc[myIndex]
/*Code to dismiss keyboard on background tap
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(LoginViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
*/
//reference to database
self.dbReference = FIRDatabase.database().reference()
//reference to username in database
self.usersReference = dbReference.child("MH_Accounts").child(userNames[myIndex])//.queryOrdered(byChild: "username")
//data comes into a snapshot object
//self.usersReference.observeSingleEvent(of: .value, with: { (snapshot) in
//data comes into a snapshot object
self.usersReference.observe(.childAdded, with: { (snapshot) in
let snapshotValue = snapshot.value as! [String: AnyObject]
//use the key "username" to get the details of username
self.dbWeight = snapshotValue["weight"]! as! String
self.dbAge = snapshotValue["age"]! as! String
self.dbHeight = snapshotValue["height"]! as! String
self.dbActivityLevel = snapshotValue["activitylevel"]! as! String
self.lblUsername.text = userNames[myIndex]
self.lblActivityLevel.text = self.dbActivityLevel
self.lblWeight.text = self.dbWeight
self.lblAge.text = self.dbAge
self.lblHeight.text = self.dbHeight
})
JSON of firebase:
{
"MH_Accounts" : {
"admin" : {
"activitylevel" : "Light",
"age" : "44",
"email" : "f#f.com",
"gender" : "Male",
"height" : "170",
"password" : "password",
"weight" : "45"
},
"test123" : {
"activitylevel" : "Light",
"age" : "21",
"email" : "iO#hotmail.com",
"gender" : "Male",
"height" : "171",
"password" : "password",
"weight" : "83"
}
},
"MH_PlanRequest" : {
"test111" : {
"date" : "03-25-2016",
"plandesc" : "haha test2",
"plantype" : "Bulking",
"username" : "test111"
},
"test333" : {
"date" : "04-26-2017",
"plandesc" : "haha test",
"plantype" : "Weight Loss",
"username" : "test333"
}
},
"MH_Progress" : {
"admin" : {
"04-23-2017" : "https://firebasestorage.googleapis.com/v0/b/myhealth-5ee8b.appspot.com/o/images%2Fadmin04-23-2017.jpg?alt=media&token=c402f576-4e29-4809-9cb3-3c8849708129"
}
}
}
The brackets indicate that the type is an array of Strings. It looks like self.usersReference is a String and .child(usernames) returns an array. You must work out whether you want to get an element from the array or if you're calling the right method in the first place.
I don't know Firebase so can't help there.

Why do I get a 'Expression type '(_) -> _' is ambiguous without more context' error

I'm adapting an example of MVVM to my app and I'm getting an 'Expression type '(_) -> _' is ambiguous without more context' error even though I've structured my code exactly like the example. Here's the relevant code (Swift 2.3):
The view controller:
class CalculatorViewController: UIViewController, CalculatorViewModelDelegate, UITextFieldDelegate, DismissalDelegate {
let dataManager = LiftEventDataManager()
#IBOutlet weak var liftNameButtonView: UIView!
#IBOutlet weak var liftNameButton: UIButton!
#IBOutlet weak var repsLabel: UILabel!
#IBOutlet weak var repetitionsField: UITextField!
#IBOutlet weak var weightLabel: UILabel!
#IBOutlet weak var weightLiftedField: UITextField!
#IBOutlet weak var oneRepMaxField: UILabel!
#IBOutlet weak var units: UILabel!
var weightLifted: String {
return weightLiftedField.text!
}
var repetitions: String {
return repetitionsField.text!
}
var viewModel: CalculatorViewModelProtocol! {
didSet {
// This is were I get the 'Expression type '(_) -> _' is ambiguous without more context' error
self.viewModel.weightLiftedDidChange = { [unowned self] viewModel in
self.weightLiftedField.text = viewModel.greeting
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let viewModel = CalculatorViewModel(delegate: self)
self.viewModel = viewModel
weightLiftedField.addTarget(self, action: #selector(didChangeWeightLifted), forControlEvents: .EditingDidEndOnExit)
}
func didChangeWeightLifted() {
viewModel!.weightLifted = self.weightLifted
}
}
And my view model:
protocol CalculatorViewModelProtocol: class {
weak var delegate: CalculatorViewModelDelegate? { get set }
var liftName: String? { get set }
var weightLifted: String? { get set }
var repetitions: String? { get set }
var oneRepMax: String? { get set }
var units: String? { get set }
var date: String? { get set }
// the type is clearly defined here
var weightLiftedDidChange: ((CalculatorViewModelProtocol) -> ())? { get set }
var repetitionsDidChange: ((CalculatorViewModelProtocol) -> ())? { get set }
//var oneRepMaxDidChange: ((CalculatorViewViewModel) -> ()) { get set }
func calculateOneRepMax(weightLifted: Double, repetitions: UInt8)
}
class CalculatorViewModel: CalculatorViewModelProtocol, LiftEventDataManagerDelegate {
let calculator = CalculatorBrain()
private let dataManager = LiftEventDataManager()
var liftName: String?
var weightLifted: String?
var repetitions: String?
var oneRepMax: String?
var units: String?
var date: String?
var weightLiftedDidChange: ((CalculatorViewModelProtocol) -> ())?
var repetitionsDidChange: ((CalculatorViewModelProtocol) -> ())?
func changeWeightLifted(weight: String) {
weightLifted = weight
}
}
Xcode seems to be telling me that it can't infer the type of self.viewModel.weightLiftedDidChange but I've specified the type in my viewModel class.
There are a lot of threads about this error but I haven't found one that has helped and I've scoured Apple's documentation but, no joy.