Initializing an instance of struct and getting unresolved identifier error - ios

I want to initialize an instance of a struct called TaskList in my TableViewController, but I'm getting a "Use of unresolved identifier 'tasks'" error every place I used 'tasks'. It worked fine when I was declaring the var tasks within the class, but now that it's an initialization of a var declared in another .swift file, I'm getting that error. I'm just learning Swift so I suspect this has something to do with the architecture or messing up how to call an object from another file. Does anyone know what I need to do to fix it? Any help would be greatly appreciated! Thanks everybody!
Here's the UITableViewController code:
import UIKit
class LoLFirstTableViewController: UITableViewController {
//var tasks:[Task] = taskData
// Hashed out the above line (even though it worked) because replacing with
// the below line because trying to get instance of TaskList containing all
// properties instead of just the tasks as well as to allow multiple instances
// of TaskList
let exampleList = TaskList(buddy: exampleBuddy, phoneNumber: examplePhoneNumber, tasks: exampleTaskData)
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 60.0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasks.count
}
#IBAction func cancelToLoLFirstTableViewController(_ segue:UIStoryboardSegue) {
}
#IBAction func saveAddTask(_ segue:UIStoryboardSegue) {
if let AddTaskTableViewController = segue.source as? AddTaskTableViewController {
if let task = AddTaskTableViewController.task {
tasks.append(task)
let indexPath = IndexPath(row: tasks.count-1, section: 0)
tableView.insertRows(at: [indexPath], with: .automatic)
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell", for: indexPath) as! TaskCell
let task = tasks[indexPath.row]
cell.task = task
if cell.accessoryView == nil {
let cb = CheckButton()
cb.addTarget(self, action: #selector(buttonTapped(_:forEvent:)), for: .touchUpInside)
cell.accessoryView = cb
}
let cb = cell.accessoryView as! CheckButton
cb.check(tasks[indexPath.row].completed)
return cell
}
func buttonTapped(_ target:UIButton, forEvent event: UIEvent) {
guard let touch = event.allTouches?.first else { return }
let point = touch.location(in: self.tableView)
let indexPath = self.tableView.indexPathForRow(at: point)
var tappedItem = tasks[indexPath!.row] as Task
tappedItem.completed = !tappedItem.completed
tasks[indexPath!.row] = tappedItem
tableView.reloadRows(at: [indexPath!], with: UITableViewRowAnimation.none)
}
}
And here's the code for the TaskList struct, if that helps:
import UIKit
struct TaskList {
var buddy: String?
var phoneNumber: String?
var tasks: [Task]
init(buddy: String?, phoneNumber: String?, tasks: [Task]) {
self.buddy = buddy
self.phoneNumber = phoneNumber
self.tasks = tasks
}
}

In your struct the tasks array is not optional it means you have to pass an initialised tasks Array. Pass an initialised array or change your task array to optional as you have done with buddy and phoneNumber.
import UIKit
struct TaskList {
var buddy: String?
var phoneNumber: String?
// add a question mark to make your array of tasks optional
var tasks: [Task]?
init(buddy: String?, phoneNumber: String?, tasks: [Task]) {
self.buddy = buddy
self.phoneNumber = phoneNumber
self.tasks = tasks
}
}
Note: when you're using struct you can leave out the initialzer it will generate one automatically
import UIKit
struct TaskList {
var buddy: String?
var phoneNumber: String?
var tasks: [Task]?
}
now in your viewController initialise the your example list
// as your tasks array is optional now even if you pass in a nil it will not crash but it will not have a tasks array
let exampleList = TaskList(buddy: exampleBuddy, phoneNumber: examplePhoneNumber, tasks: exampleTaskData)
remember to unwrap your array before using it otherwise your app can crash again if tasks array is nil. use if let or guard
if let tasks = exampleList.tasks {
// now you can use your tasks array
}

Related

iOS RealmSwift storage with Swift 4

I am trying to use RealmSwift in order to save items to the phone storage in Swift 4. I have two different Views; one for the save functionality and another which will display all saved items into a TableView. I have a buildable form coded but i am throwing an error Thread 1: signal SIGABRT specifically on the line when i call realm.add. When i am in my view which is saving, i am using a IBAction with a button to initiate the save functionality. Can anyone help me with this issue? I think the issue is when i set the var of realm however i am unsure.
UPDATE:
I have changed my implementation to reflect the idea given in this thread about my original issue. After doing so, when the call to add the item to the realm is called i crash EXC_BAD_ACCESS (code=EXC_I386_GPFLT) inside the source code of the API. Specifically I crash at this function of the API
//CODE EXCERPT FROM REALMSWIFT API
// Property value from an instance of this object type
id value;
if ([obj isKindOfClass:_info.rlmObjectSchema.objectClass] &&
prop.swiftIvar) {
if (prop.array) {
return static_cast<RLMListBase *>(object_getIvar(obj,
prop.swiftIvar))._rlmArray;
}
else { // optional
value = static_cast<RLMOptionalBase *>(object_getIvar(obj,
prop.swiftIvar)).underlyingValue; //CRASH OCCURS HERE!!!!!!!!
}
}
else {
// Property value from some object that's KVC-compatible
value = RLMValidatedValueForProperty(obj, [obj
respondsToSelector:prop.getterSel] ? prop.getterName : prop.name,
_info.rlmObjectSchema.className);
}
return value ?: NSNull.null;
import UIKit
import RealmSwift
class DetailsViewController: UIViewController {
var titleOfBook: String?
var author: String?
#IBAction func SavetoFavorites(_ sender: Any) {
DispatchQueue.global().async { [weak self] in
guard let strongSelf = self else { return }
guard let realm = try? Realm() else {
return
}
let newItem = Favorites()
newItem.title = strongSelf.titleOfBook
newItem.author = strongSelf.author
try? realm.write {
realm.add(newItem) // Crashes on this line
}
}
}
import UIKit
import RealmSwift
final class Favorites: Object {
var title: String?
var author: String?
}
class FavoritesTableViewController: UITableViewController {
var items: Array<Favorites> = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier:
"cell")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 0
}
override func tableView(_ tableView: UITableView?,
numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt
indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",
for: indexPath)
let item = items[indexPath.row]
cell.textLabel?.text = item.title
cell.detailTextLabel?.text = item.author
return cell
}
var selectedIndexPath: NSIndexPath = NSIndexPath()
override func tableView(_ tableView: UITableView, willSelectRowAt
indexPath: IndexPath) -> IndexPath? {
selectedIndexPath = indexPath as NSIndexPath
return indexPath
}
You have to wrap realm.add(newItem) inside a transaction:
try! realm.write {
realm.add(newItem)
}
Please note, that write transactions block the thread they are made on so if you're writing big portions of data you should do so on background thread (realm has to be instantiated on that thread too). You could do it like this:
#IBAction func saveToFavorites(_ sender: Any) {
DispatchQueue.global().async { [weak self] in
guard let strongSelf = self else { return }
guard let realm = try? Realm() else {
// avoid force unwrap, optionally report an error
return
}
let newItem = Favorites()
newItem.title = strongSelf.titleOfBook
newItem.author = strongSelf.author
try? realm.write {
realm.add(newItem)
}
}
}
Update: I haven't noticed that you have an issue with your model too – since Realm is written with Objective C you should mark your model properties with #objc dynamic modifiers:
final class Favorites: Object {
#objc dynamic var title: String?
#objc dynamic var author: String?
}
All changes to Realm managed objects (either creation, modification or deletion) need to happen inside write transactions.
do {
try realm.write {
realm.add(newItem)
}
} catch {
//handle error
print(error)
}
For more information, have a look at the writes section of the official docs.
Another problem you have in there is that in your Favorites class properties are missing #objc dynamic attributes. You can read about why you need that in realm docs.
Your code should look like this then:
final class Favorites: Object {
#objc dynamic var title: String?
#objc dynamic var author: String?
}

I tried to retrieving data from firebase database to tableview but I just got one element

I tried to retrieving data from Firebase database to tableview in Xcode
but I just got one element even if I have a lot of element in the database.
I followed a tutorial, I put return sonsList.count to numberOfRowsInSection as suppose but nothing happen.
Here is my code:
import UIKit
import Firebase
import FirebaseDatabase
class sons {
let name : String!
//let place : String!
init(title_String : String!){
self.name = title_String
// self.place = place_String
}
}
class sonsTableViewController: UITableViewController {
var ref:DatabaseReference!
//var sons = [String]()
var newSon: String = ""
let cellId = "cellId"
var refHandel : uint!
var sonsList = [sons]()
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
ref.child("name").queryOrderedByKey().observeSingleEvent(of: .childAdded, with: { snapshot in
let value = snapshot.value as? NSDictionary
let name = value!["name"] as! String
self.sonsList.append(sons(title_String : name))
self.tableView.reloadData()
})
//fetchName()
}
func fetchName() {
}
#IBAction func cancel(segue:UIStoryboardSegue) {
}
#IBAction func done(segue:UIStoryboardSegue) {
var sonDetailVC = segue.source as! addSonViewController
newSon = sonDetailVC.name
// sons.append(newSon)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sonsList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
let label = cell?.viewWithTag(1) as! UILabel
label.text = sonsList[indexPath.row].name
return cell!
}
}
You have issues in your Database query.
You append only one value in sonsList.
ref = Database.database().reference()
ref.child("name").queryOrderedByKey().observeSingleEvent(of: .childAdded, with: { snapshot in
//Parse snapshot value correctly it is array or not.
if let dicValue = snapshot.value as? [String : Any] {
for (key,value) in dicValue {
let name = value["name"] as? String
self.sonsList.append(sons(title_String : name))
}
self.tableView.reloadData()
}
})
Please refer this link for Get data in firebase Database.
https://firebase.google.com/docs/database/ios/read-and-write

Unable to download firebase database to my app

I am new to programming in swift. I am trying to make an app which downloads student data from firebase database. I am unable to get the app running. This is my JSON file:
{"classA":[
{
"name": "Student1",
"USN": "1DS16CS095"
},
{
"name": "student2",
"USN": "1DS16CS065"
},
{
"name":"student3",
"USN":"1DS16CS047"
}
]
}
This is my code to download the above JSON file and put it in tableView. Modelstudent is my class where I have my variables name and USN stored. and marksstudentlistTableViewCell is the class I am using to manipulate the labels of my prototype cell.
import UIKit
import Firebase
import FirebaseDatabase
struct StudentData {
let StudentName : String
let StudentUSN : String
}
class marksstudentlist: UIViewController, UITableViewDelegate, UITableViewDataSource{
var FetchedStudentIDs = [StudentData]()
#IBOutlet weak var tableView: UITableView!
var ref: DatabaseReference!
var _selectedsub: Int!
var selectedsub: Int {
get {
return _selectedsub
}set {
_selectedsub = newValue
}
}
override func viewDidLoad() {
tableView.delegate = self
tableView.dataSource = self
if(classselected==0){
ref = Database.database().reference().child("classA")
ref.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount>0{
for students in snapshot.children.allObjects as! [DataSnapshot]{
let studentObject = students.value as? [String: String]
let studentname = studentObject?["name"]
let studentUSN = studentObject?["USN"]
let student = Modelstudent(name: studentname , USN: studentUSN)
self.FetchedStudentIDs.insert(StudentData(StudentName :(studentname as? String!)! , StudentUSN : (studentUSN as? String!)! ) , at: 0)
}
self.tableView.reloadData()
}
})
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return FetchedStudentIDs.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "marksstudentlistcell", for: indexPath) as! marksstudentlistTableViewCell
cell.namelbl.text = FetchedStudentIDs[indexPath.row].StudentName// Student name comes from Firebase
cell.USNlbl.text = FetchedStudentIDs[indexPath.row].StudentUSN
return cell
}
#IBAction func backbtn(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
}
Using Swift Struct is the best way to get your firebase data inside the tableView,
Let's Get started
on your TableView swift file, above the class and stuff paste this
Struct StudentData {
let StudentObject : String // If it's
let StudentName : String // It returns a string right?
let StudentUSN : String
// do the others.
}
okay so then create a var just down below the class call it
var FetchedStudentIDs = [StudentData]()
then you got the reading method from firebase
ref = Database.database().reference().child("classA")
ref.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount>0{
self.studentslist.removeAll()
for students in snapshot.children.allObjects as! [DataSnapshot]{
let studentObject = students.value as? [String: AnyObject]
let studentname = studentObject?["name"]
let studentUSN = studentObject?["USN"]
let student = Modelstudent(name: studentname as! String?, USN: studentUSN as! String?) //storing in Modelstudent class
self.
FetchedStudentIDs.insert(StudentData(studentname:StudentName as! String , studentUSN : StudentUSN as! String ) , at: 0) // Student name comes from the struct Above, do the others as this
}
self.tableView.reloadData() // make sure you call this
}
})
return your tableView
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return FetchedStudentIDs.count
}
your CellForRowAtIndexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
cell.exampleLabel.text = FetchedStudentIDs[Indexpath.row].StudentName// Student name comes from Firebase
return cell
}
Hope this Helps

Read data from firebase and populate TableViewCell

Hello I have a tableviewcell where i can populate it with custom data from my pc, but i can't use my firebase data on the cell that i have made. I want to fill my cell with String and Int, not only Strings. My code is:
PlacesTableViewController Class
import UIKit
import FirebaseDatabase
class PlacesTableViewController: UITableViewController {
//MARK: Properties
#IBOutlet weak var placesTableView: UITableView!
//database reference
var dbRef:FIRDatabaseReference?
var places = [Places]()
var myList:[String] = []
//handler
var handle:FIRDatabaseHandle?
override func viewDidLoad() {
super.viewDidLoad()
dbRef = FIRDatabase.database().reference()
// Loads data to cell.
loadData()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return places.count
//return myList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "PlacesTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? PlacesTableViewCell else {
fatalError("The dequeued cell is not an instance of PlacesTableView Cell.")
}
let place = places[indexPath.row]
cell.placeLabel.text = place.name
cell.ratingControl.rating = place.rating
//cell.placeLabel.text = myList[indexPath.row]
//cell.ratingControl.rating = myRatings[indexPath.row]
return cell
}
//MARK: Private Methods
private func loadData() {
handle = dbRef?.child("placeLabel").observe(.childAdded, with: { (snapshot) in
if let item = snapshot.value as? String
{
self.myList.append(item)
self.placesTableView.reloadData()
print (item)
}
})
/* handle = dbRef?.child("rating").observe(.childAdded, with: { (snapshot) in
if let item = snapshot.value as? String
{
self.myList.append(item)
self.placesTableView.reloadData()
}
})*/
/*guard let place1 = Places(name: "Veranda", rating: 4) else {
fatalError("Unable to instantiate place1")
}
places += [place1]*/
}
}
Places Class
import UIKit
class Places {
//MARK: Properties
var name: String
var rating: Int
//MARK:Types
struct PropertyKey {
static let name = "name"
static let rating = "rating"
}
//MARK: Initialization
init?(name: String, rating: Int) {
// Initialize stored properties.
self.name = name
self.rating = rating
// Initialization should fail if there is no name or if the rating is negative.
// The name must not be empty
guard !name.isEmpty else {
return nil
}
// The rating must be between 0 and 5 inclusively
guard (rating >= 0) && (rating <= 5) else {
return nil
}
}
}
PlacesTableViewCell Class
import UIKit
import FirebaseDatabase
class PlacesTableViewCell: UITableViewCell, UITableViewDelegate {
//MARK: Properties
#IBOutlet weak var placeLabel: UILabel!
#IBOutlet weak var ratingControl: RatingControl!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Firebase Database
Assuming your database layout should instead look like this (see comments above):
...
placeLabel
|
-- XXY: "Veranda"
-- YYY: "Dio Con Dio"
rating
|
-- XXX: 4
-- YYY: 1
...
then try this:
private func loadData() {
dbRef!.child("placeLabel").observe(.childAdded) {
(snapshot) in
let label = snapshot.value as! String
self.updatePlace(snapshot.key, label: label)
}
dbRef!.child("rating").observe(.childAdded) {
(snapshot) in
let rating = snapshot.value as! Int
self.updatePlace(snapshot.key, rating: rating)
}
}
private var loadedLabels = [String: String]()
private var loadedRatings = [String: Int]()
private func updatePlace(_ key: String, label: String? = nil, rating: Int? = nil) {
if let label = label {
loadedLabels[key] = label
}
if let rating = rating {
loadedRatings[key] = rating
}
guard let label = loadedLabels[key], let rating = loadedRatings[key] else {
return
}
if let place = Places(name: label, rating: rating) {
places.append(place)
placesTableView.reloadData()
}
}
By the way, you can temporarily hack your database — using Firebase (nice!) web console — if you want to quickly validate the above solution.
Writing to Database. Try the following code to write the nodes in your database (i.e., this code reuses the same key across all place properties):
let key = dbRef!.child("placeLabel").childByAutoId().key
dbRef!.child("placeLabel").child(key).setValue(placeLab‌​el.text)
dbRef!.child("comment").child(key).setValue(commentText‌​Field.text)
dbRef!.child("rating").child(key).setValue(ratingContro‌​l.rating)
Hacking the Database. To edit the database manually, try:
open http://console.firebase.google.com
select your app
open database option
add a new node with the right key
delete the old node

Missing argument for parameter in call

New to swift (3) and Xcode (8) and I'm using firebase to load some data in a tableView . When I try to build the app, I get the error: "Missing argument for parameter name in call" in my fetchWhiskey function on the line when I call an instance of WhiskeyItem. I can't figure out why this error is happening. can anyone help me out?
Here's my class:
import UIKit
class WhiskeyItem {
let wName: String
let wType: String
init(wName: String, wType: String) {
self.wName = wName
self.wType = wType
}
}
and here's the tableView that I'm trying to load the data in:
import UIKit
import Firebase
import FirebaseDatabase
class FirstViewTableViewController: UITableViewController, UISearchBarDelegate {
let whiskeySearchBar = UISearchBar()
var ref: FIRDatabaseReference?
var refHandle: UInt!
var whiskeyList = [WhiskeyItem]()
let cell = "cell"
override func viewDidLoad() {
super.viewDidLoad()
createWhiskeySearchBar()
//Display Firebase whiskey data:
ref = FIRDatabase.database().reference()
fetchWhiskey()
}
func createWhiskeySearchBar() {
whiskeySearchBar.showsCancelButton = false
whiskeySearchBar.placeholder = "Search whiskeys"
whiskeySearchBar.delegate = self
self.navigationItem.titleView = whiskeySearchBar
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return whiskeyList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
// Configure the cell...
cell.textLabel?.text = whiskeyList[indexPath.row].wName
return cell
}
func fetchWhiskey() {
refHandle = ref?.child("whiskey").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String : AnyObject] {
print(dictionary)
let whiskeyItemInstance = WhiskeyItem()
whiskeyItemInstance.setValuesForKeys(dictionary)
self.whiskeyList.append(whiskeyItemInstance)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
})
}
Your initializer has two parameters which are required when calling it.
Calling it properly would look something like this:
let whiskeyItemInstance = WhiskeyItem(wName: "name", wType: "type")
If you don't want to pass parameters to the initializer, you could provide default params:
init(wName: String = "default name", wType: String = "default type") {
or use an initializer with no parameters at all:
init() {
self.wName = "wName"
self.wType = "wType"
}
or call the initializer you already created like so:
convenience init() {
self.init(wName: "default name", wType: "default type")
}
Or you could forgo initializers altogether:
class WhiskeyItem {
let wName: String = "asdf"
let wType: String = "asdf"
}

Resources