Reading an array in Firebase - arrays

I need some help in reading the "users" array in my Firebase structure as shown below.
I use the below function to grab the data from the channels table.
func observeChannels(channelId: String, onSuccess: #escaping (ChannelModel) -> Void) {
DB_REF_CHANNELS.child(channelId).observeSingleEvent(of: .value) { (snapshot) in
if let dict = snapshot.value as? [String: Any] {
let channel = ChannelModel.transformChannel(dict: dict, key: snapshot.key)
onSuccess(channel)
}
}
}
This is then passed to the ChannelModel class so I can grab each individual data.
class ChannelModel {
var channelId: String?
var channelName: String?
var ownerId: String?
var users: Dictionary<String, Any>? // Array of users }
extension ChannelModel {
static func transformChannel(dict: [String: Any], key: String) -> ChannelModel {
let channel = ChannelModel()
channel.channelId = key
channel.channelName = dict["channelName"] as? String
channel.ownerId = dict["ownerId"] as? String
channel.users = dict["users"] as? Dictionary<String, Any>
return channel
}}
The observeChannels function is then called which returns me an object of all the channels.
observeChannels(channelId: channelId, onSuccess: { (channel) in
self.channels.insert(channel, at: 0)
self.tableView.reloadData()
let user = channel.users!
print(user)
})
When I run the above function to get the users, my output is as follows:
["pZaEJ5aAAkR7WzgIJ4Wqf10jXot1": 1, "asdasldljAlsjkasldj": 1]
I was wondering how would I just get the key value - i.e:
["pZaEJ5aAAkR7WzgIJ4Wqf10jXot1", "asdasldljAlsjkasldj"]
Thanks in advance!

Try this:
let usersDictionary = channel.users!
usersArray = Array(usersDictionary.keys)
print(usersArray)
instead of:
let user = channel.users!
print(user)

Related

Child Updated crashing table (indexPath is out of range error that crashes my app Error)

I am having a hard time updating an array in my app that controls a table. I want to be able to change a child node in Firebase Database and have it update on my users' end without me having to code and send an update. I need to be able to change their tag and have it update the table without the index being out of range.
Here is my Class Struct for the information for the users:
import UIKit
class User: NSObject {
var Name: String?
var Email: String?
var UID: String?
var Tag: String?
}
In my controller I have the following code to initialize the table with the info from Firebase:
func CreateDataArray() {
Database.database().reference().child("Users").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = User()
//self.setValuesForKeys(dictionary)
user.Name = dictionary["Name"] as? String
user.Email = dictionary["Email"] as? String
user.UID = dictionary["UID"] as? String
user.Tag = dictionary["Tag"] as? String
if user.Tag == "Macro" {
self.macroUsersArray.append(user)
} else if user.Tag == "Micro" {
self.microUsersArray.append(user)
}
self.users.append(user)
self.userName.append(user.Name!)
self.filteredNames.append(user.Name!)
print(dictionary)
print(self.macroUsersArray)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}, withCancel: nil)
}
I then have a function that listens for any changes from my Firebase and updates the arrays:
func updateDataArray() {
Database.database().reference().child("Users").observe(.childChanged, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = User()
//self.setValuesForKeys(dictionary)
user.Name = dictionary["Name"] as? String
user.Email = dictionary["Email"] as? String
user.UID = dictionary["UID"] as? String
user.Tag = dictionary["Tag"] as? String
if user.Tag == "Macro" {
self.microUsersArray.remove(at: 2)
} else if user.Tag == "Micro" {
self.macroUsersArray.remove(at: 2)
}
self.users.append(user)
self.userName.append(user.Name!)
self.filteredNames.append(user.Name!)
print(dictionary)
print(self.macroUsersArray)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}, withCancel: nil)
}
I want to get the user that originally had the tag "macro" that got switch to "micro" to be removed from the macro list and show up in the micro list. I am not able to get the arrays to append and keep getting the indexPath is out of range error that crashes my app. I really need help and have been struggling with this the last few weeks.
Edit: I have decided to use observe.value so that when I make changes it reads it. Here is my code for creating the users' info:
func CreateDataArray() {
Database.database().reference().child("Users").observe(.value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = User()
//self.setValuesForKeys(dictionary)
user.Name = dictionary["Name"] as? String
user.Email = dictionary["Email"] as? String
user.Tag = dictionary["Tag"] as? String
if user.Tag == "Macro" {
self.macroUsersArray.append(user)
} else if user.Tag == "Micro" {
self.microUsersArray.append(user)
}
self.users.append(user)
self.userName.append(user.Name!)
self.filteredNames.append(user.Name!)
print(dictionary)
print(self.macroUsersArray)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}, withCancel: nil)
}
However, I am able to pull all the data into a snapshot but it is returning nil when I try to append. I am getting an error of nil on any line of code with "append(User.Name!)
I would love to figure out how to stop this nil and allow my observer to keep listening for changes in my database.
EDIT 2:
Here are my variables:
var allUsersArray = [User]()
var macroUsersArray = [User]()
var microUsersArray = [User]()
Here is my ViewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
updateDataArray()
}
Here is my updateDataArray code:
func updateDataArray() {
Database.database().reference().child("Users").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = User()
//self.setValuesForKeys(dictionary)
user.Name = dictionary["Name"] as? String
user.Email = dictionary["Email"] as? String
user.UID = dictionary["UID"] as? String
user.Tag = dictionary["Tag"] as? String
self.allUsersArray.append(user)
self.users.append(user)
self.userName.append(user.Name!)
self.filteredNames.append(user.Name!)
self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }
self.observeChangeInUserProperty()
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}, withCancel: nil)
}
Here is the observeChangeInUserProperty() function:
func observeChangeInUserProperty() {
let ref = Database.database().reference().child("Users").child("Talent")
ref.observe(.childChanged, with: { snapshot in
let key = snapshot.key
let tag = snapshot.childSnapshot(forPath: "Tag").value as! String // ! = never optional
//get the user from the allUsersArray by its key
if let user = self.allUsersArray.first(where: { $0.Tag == key }) {
if user.Tag != tag { //if the tag changed, handle it
user.Tag = tag //update the allUsersArray
if tag == "Macro" { //if the new tag is Macro remove the user from the Micro array
if let userIndex = self.microUsersArray.firstIndex(where: { $0.Tag == key }) {
self.microUsersArray.remove(at: userIndex)
self.macroUsersArray.append(user) //add user to macro array
}
} else { //new type is micro so remove from macro array
if let userIndex = self.macroUsersArray.firstIndex(where: { $0.Tag == key }) {
self.macroUsersArray.remove(at: userIndex)
self.microUsersArray.append(user)
}
}
//reload the tableviews to reflect the changes
self.microTableView.reloadData()
self.tableView.reloadData()
}
}
})
}
I am getting empty arrays for Macro and Micro. I don't know what I am doing wrong.
EDIT 3:
Here is my new user struct with initialization from Firebase:
import UIKit
import Firebase
class User: NSObject {
var Name: String?
var Email: String?
var UID: String?
var Tag: String?
init?(from snapshot: DataSnapshot) {
let dictionary = snapshot.value as? [String: Any]
self.Name = dictionary!["Name"] as? String
self.Email = dictionary!["Email"] as? String
self.UID = dictionary!["UID"] as? String
self.Tag = dictionary!["Tag"] as? String
}
}
I am now able to append the arrays but they are not populating my tables. I can also change the user's tag in Firebase without the app crashing but it doesn't observe the changed Tag and move the user to the other array. So I need help with those two things.
This answer is going to require a little set up and restatement of the goal
The goal is to have two arrays, used as dataSources for two
tableViews. One array contains user type: macro and the other array
contains user type: micro.
If a user type is changed from macro to micro (and vice-versa) the OP
wants to remove the user from one table and add it to the other.
the conceptual answer is: add an observer to the users node in Firebase and when a change occurs to a user, see if it's the type property, and if so remove from one table and add it to another.
There are lots of solutions but let me present this very long-winded answer which could be reduced in both code and by property observers.
Start with three arrays, one to hold all users and the others to hold macro and micro users. Assume the UserClass has a String type property of either macro or micro
var allUsersArray = [UserClass]()
var macroArray = [UserClass]()
var microArray = [UserClass]()
Read in all users and then from that, divide the users into their respective type
...read firebase single event of .value, with: { snapshot in
and populate allUsersArray from the snapshot..
self.macroArray = self.allUsersArray.filter { $0.type == "macro" }
self.microArray = self.allUsersArray.filter { $0.type == "micro" }
self.observeChangeInUserProperty()
}
The below code adds an observer to the users array which will notify the app when any property of a user changes, and presents that user node in the snapshot.
func observeChangeInUserProperty() {
let ref = self.ref.child("users")
ref.observe(.childChanged, with: { snapshot in
let key = snapshot.key
let type = snapshot.childSnapshot(forPath: "type").value as! String // ! = never optional
//get the user from the allUsersArray by its key
if let user = self.allUsersArray.first(where: { $0.key == key }) {
if user.type != type { //if the type changed, handle it
user.type = type //update the allUsersArray
if type == "macro" { //if the new type is macro remove the user from the micro array
if let userIndex = self.microArray.firstIndex(where: { $0.key == key }) {
self.microArray.remove(at: userIndex)
self.macroArray.append(user) //add user to macro array
}
} else { //new type is micro so remove from macro array
if let userIndex = self.macroArray.firstIndex(where: { $0.key == key }) {
self.macroArray.remove(at: userIndex)
self.microArray.append(user)
}
}
//reload the tableviews to reflect the changes
self.microTable.reloadData()
self.mactoTable.reloadData()
}
}
})
}
As mentioned this is a lot of extraneous code and could be condensed with property observers or leveraging a single array for both tables or a number of other solutions.
Edit
In case you want to know how to populate the all users array from Firebase, you need to have a User class that is init'ed from a snapshot and here's the code
func loadAllUsersAndPopulateArray() {
let ref = self.ref.child("users")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
for userSnap in allUsersSnapshot {
let user = UserClass(withSnap: userSnap)
self.allUsersArray.append(user)
}
})
}

How to access first User in an Array

I'm trying to download user from Firebase and save them in an array using a Usermodel.
The download works so far, but how do I access the first user and show the picture of this user?
My Usermodel: Sorry for some German words
class UserModel {
var username: String?
var email: String?
var profilImageUrl: String
var birthdayDate: String?
var gender: String?
var userDescription: String?
init(dictionary: [String: Any]) {
username = dictionary["username"] as? String
email = dictionary["email"] as? String
profilImageUrl = dictionary["profilImageURL"] as? String ?? ""
birthdayDate = dictionary["Geburtsdatum"] as? String
gender = dictionary["gender"] as? String
userDescription = dictionary["description"] as? String
}
In the following I´m saving the downloaded Data to the array "attendees".The picture of the first user in this array should be shown in firstUserImageView.
import SDWebImage
var attendees = [UserModel]()
#IBOutlet weak var firstUserImageView: UIImageView!
//load attendees with Api -> that works.
print(attendees.count) gives me the amount of attendees saved in the database
func loadAttendees(){
guard let eventID = event?.id else {return}
AttendeeApi.shared.observeEventAttendee(eventID: eventID) { (attendeeId) in
AttendeeApi.shared.observeAttendee(attendeeId: attendeeId, completion: { (attendee) in
self.attendees.append(attendee)
})
}
}
Set up the image view
var attendee: UserModel?{
didSet {
let firstAttendee = attendees[0].profilImageUrl
guard let firstUserImageUrl = URL(string: firstAttendee) else {return}
firstUserImageView.sd_setImage(with: firstUserImageUrl) { (_, _, _, _) in
}
}
}
loadAttendees is called in viewDidLoad but the ImageView does not show the picture of the first user.
var REF_ATTENDEE = Database.database().reference().child("users")
func observeAttendee (attendeeId: String, completion: #escaping (UserModel) -> Void) {
REF_ATTENDEE.child(attendeeId).observeSingleEvent(of: .value) { (snapshot) in
guard let dic = snapshot.value as? [String: Any] else {return}
let newAttendee = UserModel(dictionary: dic)
completion(newAttendee)
}
}
var REF_EVENTS_ATTENDEES = Database.database().reference().child("events - attendees")
func observeEventAttendee(eventID: String, completion: #escaping (String) -> Void) {
REF_EVENTS_ATTENDEES.child(eventID).observe(.childAdded) { (snapshot) in
let attendeeId = snapshot.key
completion(attendeeId)
}
}

Creating an array out of Firebase Dictionary

I am trying to retrieve a dictionary from Firebase and extract each value from the dictionary and append it to an empty array, but my code doesn't work. I haven't even added the code for appending it to an array and when I run it, "error" is printed in the console.
This is what it looks like inside Firebase
And this is what my code looks like:
func convertAllMosaicsToArray() {
// retrieve and convert here
Database.database().reference().child("mosiacTitles").observe(.value, with: { (snapshot) in
if let dictionary = snapshot.value as? [Int : AnyObject] {
print(dictionary)
} else {
print("error")
}
})
}
static func load(_ completion: #escaping () -> ()) {
if let user = Auth.auth().currentUser {
let ref = Database.database().reference().child("users").child(user.uid).child("something")
ref.observe(.value, with: { (snapshot) in
if let data = snapshot.value as? [String : AnyObject] {
var arrayNeeded: [Int] = []
if let array = data["key"] as? [Int] {
arrayNeeded = array
}
}
completion()
})
}
}
The problem was using the if let statement to cast it as a [Int : AnyObject], it just needed to be changed to an [String]
Like this:
func retrieveMosaicTitles() {
// retrieve and convert here
Database.database().reference().child("mosaicTitles").observe(.value, with: { (snapshot) in
if let allMosaicTitles = snapshot.value as? [String] {
self.searchResultsVC.listOfMosaics = allMosaicTitles
} else {
print("error")
}
})
}

How to check equality of object properties in an array of objects. Swift

I have a class called Movie, which as of now, only has a string property called movieTitle.
I have an array of Movie, and using the .contains method returns false even when an object with the same title is in the array. Interestingly enough, .contains works in a playground I made but not in an app setting.
Thanks for the help! I'm fairly new to the programing game so if you and ELI5 things, that would be great!
Here's a snippet of the code I have. What ends up happening, is it just keeps adding the same 10 entries onto the array.
do{
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String: AnyObject]
if let movieSearch = json["Search"] as? [[String: AnyObject]] {
for movie in movieSearch {
if let title = movie["Title"] as? String {
let newMovie = Movie(movieTitle: title)!
if (!self.movieList.contains(newMovie)) {
self.movieList.append(newMovie)
}
self.tableView.reloadData()
}
}
}
}catch {
print("Error with Json: \(error)")
}
Movie Class
import UIKit
class Movie: NSObject, NSCoding {
// MARK: Properties
struct PropertyKey {
static let movieTitleKey = "title"
}
// MARK: Archiving Paths
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("Movies")
var movieTitle: String
// MARK: Initialization
init?(movieTitle: String) {
// Initialize stored properties.
self.movieTitle = movieTitle
super.init()
// Initialization should fail if there is no itemName
if movieTitle.isEmpty {
return nil
}
}
// MARK: NSCoding
func encode(with aCoder: NSCoder) {
aCoder.encode(movieTitle, forKey: PropertyKey.movieTitleKey)
}
required convenience init?(coder aDecoder: NSCoder) {
let title = aDecoder.decodeObject(forKey: PropertyKey.movieTitleKey) as! String
//Must call designated initializer.
self.init(movieTitle: title)
}
}
// MARK: Equatable
func ==(lhs: Movie, rhs: Movie) -> Bool { // Implement Equatable
return lhs.movieTitle == rhs.movieTitle
}
What works in playgrounds
class Movie: NSObject {
var movieTitle: String
init?(movieTitle: String) {
// Initialize stored properties.
self.movieTitle = movieTitle
super.init()
// Initialization should fail if there is no itemName
if movieTitle.isEmpty {
return nil
}
}
}
var movieList = [Movie]()
var movie1 = Movie(movieTitle: "Batman")
var movie2 = Movie(movieTitle: "Batman")
var movie3 = Movie(movieTitle: "Superman")
movieList.append(movie1!)
movieList.append(movie2!)
movieList.contains(movie1!) // Returns True
movieList.contains(movie3!) // Returns False
Because your Movie class (why is it a class?) inherits from NSObject (why?), it inherits NSObject's conformance of the Equatable protocol, with the NSObject implementation of ==. By default, this does identity comparison (comparing references), rather than value comparison.
Here's an example:
let movie1 = Movie(movieTitle: "Batman")
let movie2 = Movie(movieTitle: "Batman")
var movieList = [Movie1]
movieList.contains(movie1!) // True, because movie1 was added in
movieList.contains(movie2!) // False, movie2 was never added
Since Movie doesn't override == with an implementation that compares its value(s) (such as movieTitle), it defers to the default implementation, which is comparing the references. Even though movie2 has the same value, it's a distinct object with its own (separate) memory location. Thus, the identity comparison fails, and it's not found.
To solve this implement == to return true iff all the fields of Movie match up. What you're trying to do may be better off being implemented with structs, however.
you should try with this way.
var filtered = [Movie]()
filtered = movieList.filter({$0.movieTitle == "Superman"})
if filtered.count == 1 {
//so,"Superman" movie contained in array..
}
let me know the results... thanks.
Just try this code it works perfectly.
do{
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String: AnyObject]
if let movieSearch = json["Search"] as? [[String: AnyObject]] {
for movie in movieSearch {
if let title = movie["Title"] as? String {
let newMovie = Movie(movieTitle: title)!
let movieTitles = (self.movieList as NSArray).value(forKeyPath: "movieTitle") as? [String]
if movieTitles == nil || movieTitles!.contains(title) == false {
self.movieList.append(newMovie)
}
self.tableView.reloadData()
}
}
}
}catch {
print("Error with Json: \(error)")
}
Try overriding isEqual method of NSObject since it is already conforming Equatable protocol. You can test the code below in a playground. Hope it helps.
class Movie: NSObject {
var movieTitle: String
init?(movieTitle: String) {
// Initialize stored properties.
self.movieTitle = movieTitle
super.init()
// Initialization should fail if there is no itemName
if movieTitle.isEmpty {
return nil
}
}
override func isEqual(_ object: Any?) -> Bool {
guard let theMovie = (object as? Movie) else { return false }
return movieTitle == theMovie.movieTitle
}
}
var movieList = [Movie]()
func appendToList(newMovie: Movie) {
if (!movieList.contains(newMovie)) {
movieList.append(newMovie)
}
}
var movie1 = Movie(movieTitle: "Batman")
var movie2 = Movie(movieTitle: "Batman")
var movie3 = Movie(movieTitle: "Superman")
appendToList(newMovie: movie1!)
movieList.count // count is 1
appendToList(newMovie: movie2!)
movieList.count // count is still 1 not incremented
movieList.contains(movie1!) // Returns true
movieList.contains(movie2!) // Returns true
movieList.contains(movie3!) // Returns false

struggling with JSON parsing in swift

I am trying to load data in JSON format from my server into IOS application.
Here is my JSON:
[
{
"BankName": "bank1",
"CurrencyName": "cur1",
"SellRate": "0.65",
"BuyRate": "0.55",
"OfficialRate": "0.6"
},
{
"BankName": "bank1",
"CurrencyName": "cur2",
"SellRate": "1.65",
"BuyRate": "1.55",
"OfficialRate": "1.6"
}
]
There are 2 files in my project:
1:
import Foundation
class Shot {
var bankName: String!
var currencyName: String!
var sellRate: String!
var buyRate: String!
var offRate: String!
init (data: NSDictionary) {
self.bankName = getStringFromJSON(data, key:"BankName")
self.currencyName = getStringFromJSON(data, key:"CurrencyName")
self.sellRate = getStringFromJSON(data, key:"SellRate")
self.buyRate = getStringFromJSON(data, key:"BuyRate")
self.offRate = getStringFromJSON(data, key: "OfficialRate")
}
func getStringFromJSON(data: NSDictionary, key: String) -> String {
if let info = data[key] as? String{
return info
}
return ""
}
}
2:
import Foundation
class JsonTest {
func loadJson(completion: ((AnyObject) -> Void)!) {
var urlString = "http://a.com/g.php"
let session = NSURLSession.sharedSession()
let sourceUrl = NSURL(string: urlString)
var task = session.dataTaskWithURL(sourceUrl!){
(data, response, error) -> Void in
if error != nil {
println(error.localizedDescription)
} else {
var error: NSError?
var jsonData = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &error) as NSArray
var shots = [Shot]()
println(jsonData)
for shot in jsonData{
let shot = Shot(data: shot as NSDictionary)
shots.append(shot)
}
println(shots) //[jsontest.Shot, jsontest.Shot]
}
}
task.resume()
}
}
I am trying to populate array automatically when my app starts. To do it I have a code in my mainViewController class.
override func viewDidLoad() {
super.viewDidLoad()
let api = JsonTest()
api.loadJson(nil)
}
The problem occurs when I try to print shots variable in the second file.
it returns [jsontest.Shot, jsontest.Shot] when I was expecting the array of dictionaries.
println(jsonData) works fine and shows JSON data from URL.
Can anybody advise what is wrong in my program?
"shots" is an array of instances of Shot, not a dictionary:
class Shot {
var bankName: String!
var currencyName: String!
var sellRate: String!
var buyRate: String!
var offRate: String!
init (data: NSDictionary) {
self.bankName = getStringFromJSON(data, key:"BankName")
self.currencyName = getStringFromJSON(data, key:"CurrencyName")
self.sellRate = getStringFromJSON(data, key:"SellRate")
self.buyRate = getStringFromJSON(data, key:"BuyRate")
self.offRate = getStringFromJSON(data, key: "OfficialRate")
}
func getStringFromJSON(data: NSDictionary, key: String) -> String {
if let info = data[key] as? String{
return info
}
return ""
}
}
var shots = [Shot]()
let urlString = "http://almazini.lt/getrates.php"
let sourceUrl = NSURL(string: urlString)
// Using NSData instead of NSURLSession for experimenting in Playground
let data = NSData(contentsOfURL: sourceUrl!)
var error: NSError?
// As I'm using Swift 1.2 I had to change "as" with "as!"
let jsonData = NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers, error: &error) as! NSArray
for shot in jsonData{
let shot = Shot(data: shot as! NSDictionary)
shots.append(shot)
}
println(shots[0].bankName)
Update for Swift 2
var shots = [Shot]()
let urlString = "http://almazini.lt/getrates.php"
// Using NSData instead of NSURLSession for experimenting in Playground
if let sourceUrl = NSURL(string: urlString) {
NSURLSession.sharedSession().dataTaskWithURL(sourceUrl, completionHandler: { (data, response, error) in
if error == nil {
if let data = data, jsonData = try? NSJSONSerialization.JSONObjectWithData(data, options: []), jsonArray = jsonData as? [NSDictionary] {
for item in jsonArray {
let shot = Shot(data: item)
shots.append(shot)
}
print(shots[0].bankName)
} else {
print("no JSON data")
}
} else {
print(error!.localizedDescription)
}
}).resume()
}
Seems like there are two problems:
You're trying to use println to debug instead of setting a breakpoint and checking your objects values.
You have not created a description or debugDescription property for your object, so println on your object is just using some default implementation.
shots is an array of your custom object, so when you call println, it's using the description for Array, which prints out the objects in the array, comma separated, and within square brackets.
The default description property for classes in Swift just prints the class name.
Ideally, you should just use a break point to check the values of your object to be certain it initialized correctly, but if it's actually important to get them to print right, it's only a matter of implementing the description property:
override var description: String {
get {
// build and return some string that represents your Shot object
}
}

Resources