So my deinit func never get called, I've already search for an answer, but none of them works for me. And the viewcontroller keep crashing because of memory issue. Thanks for your help!
Here's my code:
#IBOutlet weak var scnView: SCNView!
#IBOutlet weak var artInfoView: UIView!
#IBOutlet weak var mainTitleLbl: UILabel!
#IBOutlet weak var textView: UITextView!
#IBOutlet weak var timeLbl: UILabel!
#IBOutlet weak var stackView: UIStackView!
#IBOutlet weak var artistImg: RoundImage!
#IBOutlet weak var artistNameLbl: UILabel!
#IBOutlet weak var artistView: UIView!
var artRoomScene = ArtRoomScene(create: true)
var artImage = UIImage()
var artInfo: [Any] = []
var posts = [Art]()
var post: Art!
var user: Users!
var showInfo: Bool = false
var showSimilar: Bool = false
let alert = Alerts()
override func viewDidLoad() {
super.viewDidLoad()
scnView = self.scnView!
let scene = artRoomScene
scnView.scene = scene
scnView.autoenablesDefaultLighting = true
scnView.isJitteringEnabled = true
scnView.backgroundColor = UIColor.white
if let info = self.artInfo[1] as? Art {
let image = self.artInfo[0] as? UIImage
let height = (image?.size.height)! / 900
let width = (image?.size.width)! / 900
self.artRoomScene.setup(artInfo: image, height: height, width: width)
self.mainTitleLbl.text = info.title
let date = info.postDate/1000
let foo: TimeInterval = TimeInterval(date)
let theDate = NSDate(timeIntervalSince1970: foo)
let time = timeAgoSinceDate(date: theDate as Date, numericDates: true)
self.timeLbl.text = "\(time)"
self.textView.text = "\(info.artHeight)'H x \(info.artWidth)'W - \(info.price)$ / month - \(info.type) \n \(info.description)."
DataService.instance.REF_USERS.child("\(info.userUid)").observe(.value, with: { (snapshot) in
if let postDict = snapshot.value as? Dictionary<String, AnyObject> {
let key = snapshot.key
self.user = Users(key: key, artistData: postDict)
if let user = self.user {
self.artistNameLbl.text = user.name
self.artistImg.sd_setImage(with: URL(string: "\(user.profilePicUrl!)") , placeholderImage: UIImage(named:"Placeholder") , options: .continueInBackground)
}
}
})
}
}
deinit {
print("viewcontroller is being deallocated")
}
You may need to use a weak or unowned reference to self in the closure you are giving to your DataService. Or, you may want to look into that code and make sure that it releases it's references to this closure when you expect it to. Given the observe verb, I would expect that it holds the reference to this closure indefinitely. So I recommend this:
Use a weak reference to self in the closure
Inside your dealloc, or a some other time like viewWillDisappear, tell the DataService that you wish to unsubscribe/stop observing. This is important so that you don't end up with the opposite problem: A dangling pointer from within your closure.
For #1, the key piece is just inside your closure, Swift has a designated way to declare that self should be a weak pointer:
DataService.instance.REF_USERS.child("\(info.userUid)").observe(.value, with: { (snapshot) in
becomes
DataService.instance.REF_USERS.child("\(info.userUid)").observe(.value, with: { [weak self] (snapshot) in
Note that you could also use [unowned self], but you would be asserting that you know self will never be non-nil when this block executes. I don't think you can know that when you're passing to a 3rd party. So use [weak self] and then you'll have to treat self as an optional, which is great for this case!
This will call your deinit
weak var weakSelf = self
DataService.instance.REF_USERS.child("\(info.userUid)").observe(.value, with: { (snapshot) in
if let postDict = snapshot.value as? Dictionary<String, AnyObject>, let strongSelf = weakSelf {
let key = snapshot.key
strongSelf.user = Users(key: key, artistData: postDict)
if let user = strongSelf.user {
strongSelf.artistNameLbl.text = user.name
strongSelf.artistImg.sd_setImage(with: URL(string: "\(user.profilePicUrl!)") , placeholderImage: UIImage(named:"Placeholder") , options: .continueInBackground)
}
}
})
Related
Heres the code I have so far, now when a user inputs any letter, my label display nothing, what I would like to figure out is how to turn that nothing "", into a 0. I tried doing an if statement on my "label.txt ="'s but that didn't pan out. What would be a better way of finding my desired results?
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var game1: UITextField!
#IBOutlet weak var game2: UITextField!
#IBOutlet weak var game3: UITextField!
#IBOutlet weak var series: UILabel!
#IBOutlet weak var average: UILabel!
#IBOutlet weak var high: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func calculate(_ sender: Any) {
self.view.endEditing(true)
guard
let text1 = game1.text,
let text2 = game2.text,
let text3 = game3.text
else { return }
guard
let game1Results = Int(text1),
let game2Results = Int(text2),
let game3Results = Int(text3)
else { return }
let gameResultsArray = [game1Results, game2Results, game3Results]
let sumArray = gameResultsArray.reduce(0, +)
let positiveArray = gameResultsArray.filter {
(item: Int) -> Bool in return item > 0
}
var avgArrayValue = 0
if positiveArray.count == 0
{
avgArrayValue = 0
}else {
avgArrayValue = sumArray / positiveArray.count
}
series.text = "\(sumArray)"
average.text = "\(avgArrayValue)"
if let maximumVal = gameResultsArray.max() {
high.text = String(maximumVal)
}
}
}
Here is what you need, convert String to Int and give the default 0. Instead of using the guard let return use this method:
Instead of this:
guard let game1Results = Int(text1) else { return }
Use this:
let game1Results = Int(text1) ?? 0
I have a tableview that presents events that a user creates. When you click on one of them it takes you to a different page that presents the details of the event.
I'm using Firebase and passing the postID from the tableview to the detailed view and all the information is being passed correctly in an NSDictionary.
However, when I try to access the NSDictionary out of the viewDidLoad and in an IBAction it tells me that the NSDictionary is nil. When I check in the viewDidLoad it is not nil.
I'm very new to programming and learning along the way but I've been stuck on this for a while now and have no idea whats wrong or how I can fix it
this is my code
import UIKit
import Firebase
class BeehiveViewViewController: UIViewController {
#IBOutlet weak var eventImage: UIImageView!
#IBOutlet weak var eventName: UILabel!
#IBOutlet weak var location: UILabel!
#IBOutlet weak var eventDate: UILabel!
#IBOutlet weak var eventHost: UILabel!
#IBOutlet weak var members: UILabel!
#IBOutlet weak var joinButton: roundButton!
var beehiveID: NSDictionary?
var ref = Database.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
view.setGradientBackground(colourOne: primaryColor, colourTwo: secondaryColor)
let uid = Auth.auth().currentUser?.uid
ref.child("users").child(uid!).child(self.beehiveID?["pid"] as! String).observe(.value) { (snapshot) in
let uid = self.beehiveID!["pid"] as! String
self.beehiveID = snapshot.value as? NSDictionary
self.beehiveID?.setValue(uid, forKey: "pid")
}
let imageURL = self.beehiveID!["imageDownloadURL"] as! String
let url = URL(string: imageURL)
DispatchQueue.global(qos: .background).async {
let data = NSData(contentsOf: url!)
DispatchQueue.main.async {
self.eventImage.image = UIImage(data: data! as Data)
}
}
self.eventName.text = self.beehiveID?["eventName"] as? String
self.eventDate.text = self.beehiveID?["eventDate"] as? String
self.eventHost.text = self.beehiveID?["beehiveHost"] as? String
self.location.text = self.beehiveID?["location"] as? String
let uidd = Auth.auth().currentUser?.uid
Database.database().reference().child("users").child(uidd!).child("Posts").child(self.beehiveID?["pid"] as! String).child("Members").observe(.value) { (snapshot) in
let memberCount = snapshot.childrenCount
self.members.text = "\(memberCount)"
}
let userID = Auth.auth().currentUser?.uid
Database.database().reference().child("users").child(userID!).child("Posts").child(self.beehiveID?["pid"] as! String).observe(.value) { (snapshot) in
print(snapshot)
if (snapshot.exists()){
self.joinButton.setTitle("Remove Beehive", for: .normal)
}
else{
self.joinButton.setTitle("Join Beehive", for: .normal)
}
}
}
#IBAction func buttonPressed(_ sender: Any) {
if joinButton.titleLabel?.text == "Remove Beehive"{
let uid = Auth.auth().currentUser?.uid
let dbref = ref.child("users").child(uid!).child("Posts").child(beehiveID?["pid"] as! String)
//error is the line above that beehiveID?["pid"] is nil
dbref.removeValue()
navigationController?.popViewController(animated: true)
}
if joinButton.titleLabel?.text == "Join Beehive"{
let uid = Auth.auth().currentUser?.uid
let dbref = Database.database().reference().child("users").child(uid!).child("Posts").child("Members")
Database.database().reference().child("users").child(uid!).child("Name").observe(.value) { (nameSnapshot) in
let memberName = nameSnapshot.value as! String
let userObject = [memberName: uid]
dbref.updateChildValues(userObject as! [AnyHashable : String])
}
}
}
}
I assume that you're passing beeHive's value from the previous controller as you haven't initialised or got the values of it anywhere:-
Try having a breakpoint right before the end of viewDidLoad to double-check if the dictionary isn't nil at the block
self.beehiveID = snapshot.value as? NSDictionary
Try using a check to see if the snapshot's value is nil using 'if let' or 'guard' as you could possibly just be assigning a nil value to the NSDictionary. Also, since you're using optionals for assigning each value, it doesn't return an exception but just keeps assigning the nil value to every property
Do try this and let me know. Glad to help!
Having a problem retrieving data from Firebase in the same order that it was entered. I have tried this a number of ways using different variations of .valueAdded & .value to get this back in the same order but having no luck. Perhaps the way in which I am modelling this data is incorrect? Any help with this would be great. Thanks.
This is my Firebase Structure :
This is my data model:
struct RentalObjects {
var title = [String]()
var rentalType = [String]()
var dateAval = [String]()
var location = [String]()
var price = [String]()
var bond = [String]()
var pets = [String]()
var descripton = [String]()
}
This is my table view VC :
import UIKit
import FirebaseDatabase
class RentalTableViewVC: UIViewController, UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var rentalImage: UIImageView!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var rentalTitle: UILabel!
#IBOutlet weak var rentalPrice: UILabel!
var rentalsObject = RentalObjects()
var databaseRef:DatabaseReference?
var handle: DatabaseHandle?
var arrayOfTitles = [String?]()
var arrayOfBond = [String?]()
var arrayOfDateAval = [String?]()
var arrayOfDes = [String?]()
var arrayOfLocation = [String?]()
var arrayOfPets = [String?]()
var arrayOfPrice = [String?]()
var arrayOfRentalType = [String?]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rentalsObject.title.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell")
cell.textLabel?.text = ("Title: \(rentalsObject.title[indexPath.row]), DateAval: \(rentalsObject.dateAval[indexPath.row])")
return cell
}
#IBAction func backPressed(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.dataSource = self
databaseRef = Database.database().reference().child("Rentals")
databaseRef?.observe(.childAdded, with: { (snapshot) in
if let dictonary = snapshot.value as? [String: AnyObject] {
print(snapshot)
switch snapshot.key {
case "bond" :
_ = dictonary.map{self.rentalsObject.bond.append(($0.value as? String)!)}
// print(self.arrayOfBond)
case "dateAval" :
_ = dictonary.map{self.rentalsObject.dateAval.append(($0.value as? String)!)}
case "description" :
_ = dictonary.map{self.rentalsObject.descripton.append(($0.value as? String)!)}
case "location" :
_ = dictonary.map{self.rentalsObject.location.append(($0.value as? String)!)}
case "pets" :
_ = dictonary.map{self.rentalsObject.pets.append(($0.value as? String)!)}
case "price" :
_ = dictonary.map{self.rentalsObject.price.append(($0.value as? String)!)}
case "rentalType" :
_ = dictonary.map{self.rentalsObject.rentalType.append(($0.value as? String)!)}
case "title" :
_ = dictonary.map{self.rentalsObject.title.append(($0.value as? String)!)}
print(self.rentalsObject.title)
// _ = dictonary.map{self.arrayOfTitles.append($0.value as? String)}
// print(self.arrayOfTitles)
default:
break
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
}
}
This is an example of the output from my tableView. I am trying to get this out in the same order it came in, in this example, 1,2,3,4,5. When I add in data from the other variables to this output they all become mixed up for some reason.
This is the code where the data is sent to firebase:
class AvertisingVC: UIViewController {
#IBOutlet weak var titleField: UITextField!
#IBOutlet weak var rentalTypeField: UITextField!
#IBOutlet weak var dateField: UITextField!
#IBOutlet weak var locationField: UITextField!
#IBOutlet weak var priceField: UITextField!
#IBOutlet weak var bondField: UITextField!
#IBOutlet weak var petsAllowedField: UITextField!
#IBOutlet weak var detailedDescription: UITextField!
var databaseRef:DatabaseReference? //reference to firebase dba
override func viewDidLoad() {
super.viewDidLoad()
databaseRef = Database.database().reference().child("Rentals") //can add .child(string:root) to add root dir to dba
}
#IBAction func backBtnPressed(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
#IBAction func submitForm(_ sender: Any) { // Send data to firebase on submit
if titleField.text != nil {
databaseRef?.child("title").childByAutoId().setValue(titleField.text)
titleField.text = ""
}
if rentalTypeField.text != nil {
databaseRef?.child("rentalType").childByAutoId().setValue(rentalTypeField.text)
rentalTypeField.text = ""
}
if dateField.text != nil {
databaseRef?.child("dateAval").childByAutoId().setValue(dateField.text)
dateField.text = ""
}
if locationField.text != nil {
databaseRef?.child("location").childByAutoId().setValue(locationField.text)
locationField.text = ""
}
if priceField.text != nil {
databaseRef?.child("price").childByAutoId().setValue(priceField.text)
priceField.text = ""
}
if bondField.text != nil {
databaseRef?.child("bond").childByAutoId().setValue(bondField.text)
bondField.text = ""
}
if petsAllowedField.text != nil {
databaseRef?.child("pets").childByAutoId().setValue(petsAllowedField.text)
petsAllowedField.text = ""
}
if detailedDescription.text != nil {
databaseRef?.child("description").childByAutoId().setValue(detailedDescription.text)
detailedDescription.text = ""
}
let alertController = UIAlertController(title: "Success!", message: "You have successfully listed a rental", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "Close Alert", style: .default, handler: nil)
alertController.addAction(defaultAction)
present(alertController, animated: true, completion: nil)
}
}
When data is interpreted from network data, swift doesn't guarantee the order of the interpreted values if they're ordered in an array or dictionary. You'd have to order the values yourself, or update a cache device-side, and sort that cache each time it is updated.
There's many ways to this simply, or intricately for various use-cases. But looking at your's, i'd add a sort on wherever you're reading the tableView's datasource, before you call the delegate and datasource methods. Taking your example, if you sorted by date:
override func viewDidLoad() {
super.viewDidLoad()
databaseRef = Database.database().reference().child("Rentals")
databaseRef?.observe(.childAdded, with: { (snapshot) in
if let dictonary = snapshot.value as? [String: AnyObject] {
print(snapshot)
switch snapshot.key {
case "bond" :
_ = dictonary.map{self.rentalsObject.bond.append(($0.value as? String)!)}
// print(self.arrayOfBond)
case "dateAval" :
_ = dictonary.map{self.rentalsObject.dateAval.append(($0.value as? String)!)}
case "description" :
_ = dictonary.map{self.rentalsObject.descripton.append(($0.value as? String)!)}
case "location" :
_ = dictonary.map{self.rentalsObject.location.append(($0.value as? String)!)}
case "pets" :
_ = dictonary.map{self.rentalsObject.pets.append(($0.value as? String)!)}
case "price" :
_ = dictonary.map{self.rentalsObject.price.append(($0.value as? String)!)}
case "rentalType" :
_ = dictonary.map{self.rentalsObject.rentalType.append(($0.value as? String)!)}
case "title" :
_ = dictonary.map{self.rentalsObject.title.append(($0.value as? String)!)}
print(self.rentalsObject.title)
// _ = dictonary.map{self.arrayOfTitles.append($0.value as? String)}
// print(self.arrayOfTitles)
default:
break
}
}
whateverCollectionIsYourInCacheDataSource = whateverCollectionIsYourInCacheDataSource.sorted(by: { ($0, $1) -> Bool in
return $0 < $1 //If you want to sort them by their date value. Make sure the dates are properly interpreted from whatever format they're saved in remotely into the Date type.
})
DispatchQueue.main.async {
//No need to call .reloadData and these methods in viewDidLoad, unless you REALLY have to (ie: your data is modified on purpose after being loaded initially). Calling these methods loads up the data anyways, so just call these whenever your data is downloaded, converted and sorted properly.
tableView.dataSource = self
tableView.dataSource = self
}
})
}
Hope it helps you on the right path. Look up network-based tableview / collectionview tutorials, they can be very helpful.
I have my function here which is supposed to pull all the locations under the child "launch" in my database.
import UIKit
import Firebase
import FirebaseDatabase
class SearchSelectVC: UIViewController, UIPickerViewDelegate , UIPickerViewDataSource{
#IBOutlet weak var locationPicker: UIPickerView!
#IBOutlet weak var datePicker: UIPickerView!
#IBOutlet weak var locationLB: UILabel!
#IBOutlet weak var dateLB: UILabel!
var locData: [String] = [String]()
var dateData : [String] = [String]()
override func viewDidLoad() {
super.viewDidLoad()
initPickers()
getloc(onCompletion: { (data) in
print(data)
self.locData = (data)
})
}
func getDate(onCompletion: #escaping ([String]) -> ()) {
var data : [String] = []
let selectedLoc = locationLB.text
var dateRef: DatabaseReference!
dateRef = Database.database().reference().child("launch").child((selectedLoc)!)
dateRef.observe(.value) { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let key = snap.key
data.append(key)
print("date = \(key)")
//print(data)
}
onCompletion(data)
}
}
func getLoc(onCompletion: #escaping ([String]) -> Void){
print("here3")
var data : [String] = []
var locRef: DatabaseReference!
locRef = Database.database().reference()
print("here4")
locRef.child("launch").observe(.value) { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let key = snap.key
print(snap)
print(key)
data.append(key)
print("location = \(key)")
//print(data)
}
onCompletion(data)
}
}
func initPickers(){
locationPicker.delegate = self
locationPicker.dataSource = self
datePicker.delegate = self
datePicker.dataSource = self
}
}
None of the "here" statements get printed out, neither the snapshot of key or the data which is being collected. Im not really sure whats wrong with my code. Here is a picture of my database
Thanks!
How can i passing data uiviewController from uiview
I am Using function but it was not working
protocol name is startcalldelegate and function name is startcall
UIView Code
protocol StartCallDelegate: class {
func startCall(localNickname :String, remoteNickname :String)}
class CardView: UIView {
let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
weak var delegate: CardViewDelegate?
weak var socketdelegate: StartCallDelegate?
#IBOutlet weak var UserPhoto: UIImageView!
#IBOutlet weak var UserNickName: UILabel!
#IBOutlet weak var UserAge: UILabel!
#IBOutlet weak var UserPeople: UILabel!
var localNickname: String = ""
var remoteNickname: String = ""
#IBAction func SendMessage(_ sender: Any) {
print("SendMessage")
//print(localNickName)
//print(UserNickName.text!)
}
#IBAction func SendVideoCall(_ sender: Any) {
print("SendVideoCall")
let entityDescription = NSEntityDescription.entity(forEntityName: "Profile", in: managedObjectContext)
let request = NSFetchRequest<NSFetchRequestResult>()
request.entity = entityDescription
do {
let objects = try managedObjectContext.fetch(request)
if objects.count > 0 {
let match = objects[0] as! NSManagedObject
localNickname = match.value(forKey: "nick") as! String
} else {
print("Nothing founded")
}
} catch {
print("error")
}
remoteNickname = UserNickName.text!
socketdelegate?.startCall(localNickname: localNickname, remoteNickname: remoteNickname)
delegate?.VideoChatSegue()
}
}
UIViewcontroller Code
class ViewController: UIViewcontroller, StartCallDelegate {
var localNickname: String = ""
var remoteNickname: String = ""
override func viewDidLoad() {
super.viewDidLoad()
print(localNickname)
print(remoteNickname)
}
func startCall(localNickname: String, remoteNickname: String) {
print("Action startcall func")
self.localNickname = localNickname
self.remoteNickname = remoteNickname
}
startCall func not working
You need to define delegate in viewcontroller' ViewDidLoad
let objOardView = CardView() // this is only test purpose
objOardView.socketdelegate = self