I'm trying to find a solution for a little problem I have with my app. It's a chess app and it works with Firebase and is written in swift. I use the database for user authentication, user information and uploading moves to play against each other online. All the userInfo is saved in a document in the collection "allUsers".
Everything is working fine, but I have a user screen where you can press a refresh button to update the current online users, with the .getDocuments() function. The problem is that every time a user refreshes, they query through all of the registered accounts, check if they have the flag "isOnline = true" and then list only those users in a TableView. I believe this counts in firebase as 1 read for every registered account, even though the user is not online.
The app is already live in the AppStore and I have quite a few people registered already. About 300. But, to be honest a lot of people just try it once or a few times and then leave again and never use the app again. But every time someone wants to update the online users, they cycle through 300 users and this gives me 300 reads with firebase. Right now it's not a big problem, but once people really start to use the app, I will reach my quotum quite quickly.
I could try .addSnapshotListener , but this will refresh the user screen everytime something happens in the userlist. It will be too chaotic. I've read about reading data from cache, but I'm not sure how to go about this. I could also get a fresh userlist when the app starts, save it locally and check every now and then if there are new online users, but I want the list to be updated whenever the user wants to.
Is there a way to compare a locally saved list to the online database list and only read/get the documents that are changed / new?
Sorry for the long text. Hopefully anyone can help me out!
Thanks.
Below is my code to load the users. It's a bit messy sorry.. Basically it retrieves all users and sorts them by online and offline. If a user searches for another user, it takes a String "query" as input.
Code :
func loadAllAvailableUsers(query : String) {
availableEmails = []
availableUsers = []
onlineUsers = []
isInGameIndex = []
var av : [String] = []
var ae : [String] = []
var wins : [Int] = []
var losses : [Int] = []
var draw : [Int] = []
var matches : [Int] = []
let collection = db.collection("allUsers")
collection.getDocuments { (snapshot, err) in
if let e = err {
print(e.localizedDescription)
} else {
if let documents = snapshot?.documents {
print("found users")
for doc in documents {
print(doc.documentID)
if doc.documentID != currentEmail && query == "" {
print(doc.data()["isOnline"] as! Bool)
if let name = doc.data()["username"] as? String, let w = doc.data()["wins"] as? Int, let l = doc.data()["losses"] as? Int, let d = doc.data()["draw"] as? Int, let numOfMatches = doc.data()["numberOfMatches"] as? Int, let online = doc.data()["isOnline"] as? Bool {
print("adding user : \(name) to list")
if online {
matches.append(numOfMatches)
av.append(name)
ae.append(doc.documentID)
wins.append(w)
losses.append(l)
draw.append(d)
onlineUsers.append(name)
if doc.data()["isInGame"] as! Bool == true {
print(i)
self.delegate?.addToInGameIndex(name: name)
}
}
}
} else if query != "" {
if let name = doc.data()["username"] as? String, let w = doc.data()["wins"] as? Int, let l = doc.data()["losses"] as? Int, let d = doc.data()["draw"] as? Int, let online = doc.data()["isOnline"] as? Bool, let numOfMatches = doc.data()["numberOfMatches"] as? Int {
print("Searched : adding user : \(name) to list")
if doc.documentID == currentEmail {
continue
}
let lowerName = name.lowercased()
let lowerQuery = query.lowercased()
if lowerName.contains(lowerQuery) && online {
av.append(name)
ae.append(doc.documentID)
wins.append(w)
losses.append(l)
draw.append(d)
matches.append(numOfMatches)
if doc.data()["isInGame"] as! Bool == true {
print(i)
self.delegate?.addToInGameIndex(name: name)
}
onlineUsers.append(name)
} else if lowerName.contains(lowerQuery) && !online {
av.append(name)
ae.append(doc.documentID)
wins.append(w)
losses.append(l)
draw.append(d)
matches.append(numOfMatches)
}
}
}
}
}
if ae.count > 0 {
self.delegate?.reloadTheTable(wins: wins, losses: losses, draw: draw, ae: ae, au: av, matches: matches)
}
print(availableUsers)
}
}
}
Related
What I have: A snapshot of all users with a bunch of if statements that eventually returns an array of users that get displayed.
What I need: The array of end users to be used in a .query in the line preceding the snapshot.
Why do I need this: This line is so that the entire database of users is not run on the client.
More specifically, what do I need to query for: A) Users who have a child "caption"(timestamp) with a timestamp that is in today, AND, B) who are 3000 miles from the current user.
JSON of DB
"people" : {
"02PdiNpmW3MMyJt3qPuRyTpHLaw2" : {
"Coordinates" : {
"latitude" : -25.809620667034363,
"longitude" : 28.321706241781342
},
"PhotoPosts" : "https://firebasestorage.googleapis.com/v0/b/daylike-2f938.appspot.com/o/images%2F02PdiNpmW3MMyJt3qPuRyTpHLaw2%2FPhotoPosts?alt=media&token=24fee778-bcda-44e3-aa26-d7c2f8509740",
"caption" : 1602596281762, /// this is timestamp
"postID" : "02PdiNpmW3MMyJt3qPuRyTpHLaw2"
},
"e1" : “cvvvv666",
"e2" : "aol.com",
" "postID" : "0RnqWV7Gd9Z0bUW9nUvizMQOjK73",
"users" : "cvvvv666#aol.com"
},
.
var dict = CLLocation()
...
dict = CLLocation(latitude: lat, longitude: lon)
...
let thisUsersUid = Auth.auth().currentUser?.uid
//this line below is where the refArtists2 query should go. in other words send all users to device that meet the 2 if statements, which is represented by self.people.append(peopl)//
let refArtists2 = Database.database().reference().child("people").queryOrdered(byChild: "caption").queryEqual(toValue: ANY Timestamp in today).queryOrdered(byChild:Coordinates). queryEqual(toValue:ThoseCoordinates which make the distance to current user less than 3000 miles)
refArtists2.observe(DataEventType.value, with: { snapshot in
if snapshot.childrenCount>0{
self.people.removeAll()
for people in snapshot.children.allObjects as! [DataSnapshot] {
if people.key != thisUsersUid {
let peopleObject = people.value as? [String: AnyObject]
let peopleCoordinates = peopleObject?["Coordinates"] as? String
let peoplecaption = peopleObject?["caption"] as? Int //is timestamp
let peoplepostID = peopleObject?["postID"] as? String
let coordSnap = people.childSnapshot(forPath: "Coordinates")
guard let lat = coordSnap.childSnapshot(forPath: "latitude").value as? CLLocationDegrees else { return }
guard let lon = coordSnap.childSnapshot(forPath: "longitude").value as? CLLocationDegrees else { return }
let locCoord = CLLocation(latitude: lat, longitude: lon)
let coordSnap12 = people.childSnapshot(forPath: "caption").value as? Int ?? 0
let date = Date(timeIntervalSince1970: TimeInterval(coordSnap12)/1000.0)
//let secondsInDay = 86400
**if Calendar.current.isDateInToday(date)** {
let distance = locCoord.distance(from: self.dict)
print(distance, "distancexy")
**if distance/1609.344 < 3000**{
let peopl = Userx(Coordinates: peopleCoordinates, distance:distance, caption: peoplecaption, postID: peoplepostID)
self.people.append(peopl)
let d = people.key as! String
self.printPersonInfo(uid:d) ///////This is used to reload the data
} else {
print ("w")
}
} else {
print ("alphaaa")
}
}
print("aaaaaaaa", self.people.map {$0.distance})
}
self.people.sort { ($0.distance ?? 0) < ($1.distance ?? 0) } ////////This sorting with distance is used with returning the cell. people is used as uid array to return the cell.
}
})
} else {
print("no")
}
})
Ancillary caveat: the self.people.sort { ($0.distance ?? 0) < ($1.distance ?? 0) }sorting is important, so the queries should not impede that. I am a bit concerned with using queryOrdered in that it orders the array of users in the wrong order. If it does, a C) query should be: The order of the users must be with the closest users to the logged in user first. The furthest from the logged in user must go last in the array.
Another way of asking this would be: Instead of running a snapshot of all users, how do you query the snapshot's 'end result sort' when making the snapshot?
The timestamp is seconds since 1970
My attempt at the date query below. I took the code and tried to put the code that gets the date before the actual query(currently the code that gets the date is after the snapshot of all users).
var ppp: String! ////this should be the uid of all users in db
let people = Database.database().reference().child("people").child(self.ppp).child("captions")
people.observe(DataEventType.value, with: { snapshot in
let captionss = snapshot.value as? Int ?? 0
let date = Date(timeIntervalSince1970: TimeInterval(captionss)/1000.0)
let query1 = Database.database().reference().child("people").queryOrdered(byChild: "caption").where?(isDateInToday(date))
Edit: This answer is in Firestore, not Realtime Database. However, the concepts are the same.
The question is several questions in one; asking about distance, compound queries and how to query Firebase in general. I will post this answer to address the second two and distance queries are addressed in the comment to the question.
Once the query pattern is understood, they become easier and most importantly; it becomes more obvious that how your data is structured depends on what queries you want to run against that data.
Suppose we have a users collection with user documents - each documentId is the users uid
users
uid_0
name: "Leroy"
and then we have the posts for the users - each post contains some text, a timestamp of the post, the uid of the user that posted it, what the topic is and a url of a picture that appears in the post. Notice I am storing posts in a separate collection; why read in a bunch of user data when we only want to know about their post.
posts
post_id
postText: "pretty flowers"
postDate: "20201103"
postUrl: "www....."
postUid: "uid_0"
postTopic: "flowers"
Let suppose we want to get posts from today that are about flowers, and then also get the posters name and output who posted the message and what they said.
To do this we will need a compound query and then a subquery to retrieve the posters name as well.
func getTodaysPostsAboutFlowers() {
let postsCollection = self.db.collection("posts")
let query = postsCollection.whereField("postDate", isEqualTo: "20201103").whereField("postTopic", isEqualTo: "flowers")
query.getDocuments(completion: { snapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
guard let docs = snapshot?.documents else { return }
for doc in docs {
let postText = doc.get("postText") as? String ?? "No text"
guard let postersUid = doc.get("postUid") as? String else { return }
self.outputPostTextAndUserName(withText: postText, andUid: postersUid)
}
})
}
The above performs a compound query on both the postDate field as the postTopic field.
The above then calls another function to retrieve the users name and output both the name and what they said
func outputPostTextAndUserName(withText: String, andUid: String) {
let usersCollection = self.db.collection("users")
let theUserDoc = usersCollection.document(andUid)
theUserDoc.getDocument(completion: { documentSnapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
if let doc = documentSnapshot {
let postersName = doc.get("name") as? String ?? "No Name"
print("\(postersName) posted: \(withText)")
}
})
}
and the output
Leroy posted: pretty flowers
As you can see, there's no need to load all of the users, no need to iterate over results etc. Even if you have a billion users, this will only return a subset of that data which is a best practice when working with huge data sets; only get the data you're interested in.
Edit. The OP is asking about querying for nodes containing today. The simple solution is to have one child node containing a timestamp which would contains specific date data and then another child node just containing today data in YYYYMMDD format.
people
uid_x
timetamps: 9023490823498 //Date(timeIntervalSince1970:
todaystamp: "20201106" // yyyymmdd format
that makes querying for nodes that contain today very simple.
I want to save new objects via a view controller in my app. However, I want these new objects to load when the app is logged into. I am using firebase to save data into a database, but how can I save an object and have it return when the app is logged into again? I am new-ish to programming, sorry for any potential confusion.
Here is where the goal information is read when the app has been logged into.
for i in 0 ... clientList.count - 1 {
screenHandle = ref?.child(organizationCode).child(clientList[i].name).observe(.value, with: { (snapshot) in
let clientStuffLoad = snapshot.value as! [String:Any]
if clientStuffLoad["Goal 1 Description"] != nil {
clientList[i].goal1 = clientStuffLoad["Goal 1"] as! String
} else {
clientList[i].goal1 = ""
}
This is essentially what I have regarding adding a new member to the class Client:
#IBAction func addingClientSaveButton(_ sender: Any) {
var client7 = Client(name: addingClientName.text!,
goal1: addingClientGoal1.text!, goal2:
addingClientGoal2.text!,
goal3: addingClientGoal3.text!,
isSelected: false, s: 1,
ind: 1, targetBehavior1 : addingClientTB1.text!,
targetBehavior2 : addingClientTB2.text!,
targetBehavior3 : addingClientTB3.text!,
targetBehavior1Info : addingClientTB1Info.text!,
targetBehavior2Info : addingClientTB2Info.text!,
targetBehavior3Info : addingClientTB3Info.text!)
but I would like the object name to read the client name input as opposed to client7
The second part to this is that I want a way to write this to the database, and be able to read it at log in so that I can use the properties of the class and add to it when adding a new client.
This is a super broad question because it covers a lot of different aspects of working with Firebase; writing, reading, handling DataSnapshots etc. Also, I don't know what your data represents so I picked something for me to cover some of the aspects of working with Firebase.
There's no error checking but it works as is. I've commented along the way.
Firebase has no objects; just parent and child nodes. Everything can be thought of as key: value pairs like a dictionary. You cannot write an object or read an object. Only NSString, NSNumber, NSDictionary and the dreaded NSArray (or their Swift counterparts)
Let's start with a class - there's 100 ways to do this but I like classes to be responsible for their properties as well as accepting them and presenting them
class WineClass {
var wine_key = ""
var name = ""
var varietal = ""
//this is used when creating a new wine object before storing in firebase
init(withName: String, andVarietal: String) {
self.name = withName
self.varietal = andVarietal
}
//this is used when we are loading data from firebase to create the wineclass object
init(withSnapshot: DataSnapshot) {
let wineName = withSnapshot.childSnapshot(forPath: "wine_name").value as? String ?? "No Wine Name"
let wineDict = withSnapshot.value as! [String: Any]
let wineVarietal = wineDict["wine_varietal"] as? String ?? "No Wine Varietal"
self.wine_key = withSnapshot.key //when we read a wine, this will be it's reference in case we want to update or delete it
self.name = wineName
self.varietal = wineVarietal
}
//this is use to create a dictionary of key:value pairs to be written to firebase
func getWineDictForFirebase() -> [String: Any] {
let d = [
"wine_name": self.name,
"wine_varietal": self.varietal
]
return d
}
}
Then, we need a class var to store the WineClass's. This would be for example a dataSource for a tableView
var wineArray = [WineClass]() //a class var array to store my wines
Then I will give you two buttons, one that populates and writes some wine to Firebase and then a second that read them in and prints to console
func button0() {
self.writeWine(withName: "Scarecrow", andVarietal: "Red Blend")
self.writeWine(withName: "Ghost Horse", andVarietal: "Cabernet Sauvignon")
self.writeWine(withName: "Screaming Eagle", andVarietal: "Cabernet Sauvignon, Merlot, Cabernet Franc")
}
func button1() {
self.readWines()
}
And then the function that accepts some strings as properites for each wine and writes them to Firebase
func writeWine(withName: String, andVarietal: String) {
let newWine = WineClass(withName: withName, andVarietal: andVarietal) //create a new wine object
let wineListRef = self.ref.child("wine_list") //get a reference to my firebase wine_list
let thisWineRef = wineListRef.childByAutoId() //a new node for this wine
let d = newWine.getWineDictForFirebase() //get the wine properties as a dictionary
thisWineRef.setValue(d) //save it in firebase
}
and finally a function that reads in those wines, and prints their properties in console
func readWines() {
let wineRef = self.ref.child("wine_list")
wineRef.observeSingleEvent(of: .value, with: { snapshot in //we are reading in the entire wine node which will contain many child nodes
let allWines = snapshot.children.allObjects as! [DataSnapshot] //cast each child node as a DataSnapshot & store in array
for wineSnap in allWines { //iterate over each child node in the array
let wine = WineClass(withSnapshot: wineSnap) //create a new wine, ensuring we also keep track of it's key
self.wineArray.append(wine) //add to the array
}
for wine in self.wineArray {
print(wine.wine_key, wine.name, wine.varietal)
}
})
}
lastly, when button0 is clicked, our Firebase looks like this
wine_list
-LhbjhkEC8o9TUISCjdw
wine_name: "Scarecrow"
wine_varietal: "Red Blend"
-LhbjhkEC8o9TUISCjdx
wine_name: "Ghost Horse"
wine_varietal: "Cabernet Sauvignon"
-LhbjhkEC8o9TUISCjdy
wine_name: "Screaming Eagle"
wine_varietal: "Cabernet Sauvignon, Merlot, Cabernet Franc"
and then the output when button1 is clicked
-LhbjhkEC8o9TUISCjdw Scarecrow Red Blend
-LhbjhkEC8o9TUISCjdx Ghost Horse Cabernet Sauvignon
-LhbjhkEC8o9TUISCjdy Screaming Eagle Cabernet Sauvignon, Merlot, Cabernet Franc
Note that self.ref is a reference to the root node of my firebase yours will need to reference your firebase.
func checkPaid(utilityId : String) -> Int{
var amount:String = ""
var status = 0
print("inside new function ")
print ("\(utilityId) inside new function ")
self.databaseRefPayment.observe(DataEventType.value, with:{(DataSnapshot) in
if DataSnapshot.childrenCount > 0 {
for payments in DataSnapshot.children.allObjects as! [DataSnapshot]{
var paymentsObject = payments.value as? NSDictionary
/*
if(paymentsObject!["month"] as! String == monthCheck && paymentsObject!["year"] as! String == monthCheck && paymentsObject!["utilityid"] as! String == utilityId as! String){ */
if(paymentsObject!["utilityId"] as! String == utilityId){
amount = paymentsObject!["amount"] as! String
print(amount)
print("Ypur program is working perfect")
status = 1
}
}
}
})
return status
}
The above function is filtering the data present in payments node based on the value for utilityId getting passed in the function . But the strange thing is observe(DataEventType.value, with:{(DataSnapshot) this event is not getting triggered all the time . Its just skipping that portion unnecessarily . I am very new to firebase and getting really mad with these kind of unpredicted behaviours . Please help me in this . feel free to ask for any clarifications .
The firebase executes firebase query functions in different thread , so after u call check paid(), it runs the checkpaid() firebase query in another thread,and it will return from the function , eventhough ur query is running in the background..so it will seem like,checkpaid() is not working , but actually it's running on another thread.
I think you first fetch all the required data from payment, and store it in a list , and then use that list to compare with utility.
Every time this function is called it adds/resets the Key-Value Observer for whichever child node you are observing it doesn't actually check the value unless it is changed. I believe it is your intention to call checkPaid(utilityId:) to check the child is 'paid' by some means. There is no need to add a KVO if you are directly reading the value for a single snapshot. consider the following:
func checkPaid(utilityId: String) -> Bool {
//Assume it is not paid if we cannot verify it.
var isPaid = false
//Create a new reference to Firebase Database
var ref: DatabaseReference!
ref = Database.database().reference().child(utilityId)
//Get the values for the child, test if it is paid or not.
ref.queryOrderedByValue().observeSingleEvent(of: .value) { (snapshot) in
if (snapshot.value is NSNull) {
print("No Child With \(utilityId) Exists")
} else {
//child with utilityId exists, in case multiple utilityId's exist with the same value..
for child in snapshot.children.allObjects as! [DataSnapshot] {
if let values = child.value as? [String : AnyObject] {
let uid = child.key //utilityId
var month:String = ""
var year:String = ""
var amount:String = ""
//var amount:Double = 0.0
//get values from parent
if let m = values["month"] as? String {
month = m
}
if let y = values["year"] as? String {
year = y
}
if let a = values["amount"] as? String {
amount = a
}
/*
if let a = values["amount"] as? Double {
amount = a
}
*/
//??
if ((month == monthCheck) && (year == monthCheck)) {
isPaid = true
}
}
}
}
return isPaid
}
I am making one assumption here; that utilityId is the key for the child.
if you have parent nodes to utilityId you'll have to transverse those as well when you reference the database:
ref = Database.database().reference().child(utilities).child(utilityId) ..etc
If you need a KVO to update a local property I suggest adding/calling it in viewDidLoad, it's completion handler should take care of updating whichever properties are updated when they change in Firebase.
I asked a question yesterday that was marked as a duplicate, and when I updated the question it was not unmarked. So I am asking again here (as per stackoverflow's recommendation).
I am trying to sort by multiple values in firebase. I understand that is not possible, but i was given an example in another language which is only half helpful as how to go about doing it the right way. In any case i tried to follow the example given here Query based on multiple where clauses in firebase .
This is the structure of my firebase
room
-KJe22sduQMz1DIs_DH6
allowedParticipants:
14
createdBy:
"Mr Tester"
members:
"nmeMYnnSatRch5qKPJKIe7jEOLy2"
participating:
true
status:
"seedling"
theme:
"Cats"
totalNumberOfMembers:
1
and this is the code that I am trying to get to work
ref.queryOrderedByChild("status").queryStartingAtValue("active").queryEndingAtValue("active").observeEventType(.Value) { (snapshot: FIRDataSnapshot) in
let themeOfEvent = snapshot.value
if themeOfEvent?.value == pickedTheme {
print("foo")
}
}
Could somebody please post a useful comment or answer to help me?
Thank you
I was able to get help
This works
func listOfPossibleCompetitionsFromFirebase(){
let createdRoomRef = firebase.child("room")
createdRoomRef.queryOrderedByChild("status").queryStartingAtValue("active").queryEndingAtValue("active").observeEventType(.Value) { (snapshot: FIRDataSnapshot) in
var themeCount = 0
self.listOfOpenComps.removeAll()
if let tmp = snapshot.value as? [String:AnyObject] {
let keys = tmp.keys
for key in keys {
if let roomDetails = (tmp[key] as? [String:AnyObject]) {
if let themeOfEvent = roomDetails["theme"] as? String where themeOfEvent == pickedTheme {
themeCount += 1
self.listOfOpenComps.append(key)
}
}
}
}
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
print("rooms count: \(themeCount)")
}
}
Can anyone help me figure out why this code is behaving thusly...
When opening the app for the first time a "User" is created (if it doesn't already exist, which it doesn't the first time) and then the user is saved along with his/her golf "clubs". I get confirmation of the user saved and the clubs saved in the console. HOWEVER, when I close the app and reopen it the user is fetched but the clubs are not. What am I missing here? Let me know if you need/want to see any screen captures beyond this code...
//MARK: Core Data Variables
var user : User!
var userClubs = NSMutableSet()
var currentRound : Round!
var managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
func prepareUser() {
let fetchRequest = NSFetchRequest(entityName: "User")
let sortDescriptor = NSSortDescriptor(key: "createdTime", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
if let fetchResults = self.managedObjectContext.executeFetchRequest(fetchRequest, error: nil) as? [User] {
if fetchResults.count > 0 {
self.user = fetchResults[0]
println("First user: \(self.user!.firstName) \(self.user!.lastName)")
let fetchRequestClubs = NSFetchRequest(entityName: "Club")
if let fetchResults2 = self.managedObjectContext.executeFetchRequest(fetchRequestClubs, error: nil) as? [Club] {
if fetchResults2.count > 0 {
println("test: \(fetchResults2[0].type)")
}
}
} else {
println("No user yet")
var newUser : User = NSEntityDescription.insertNewObjectForEntityForName("User", inManagedObjectContext: self.managedObjectContext) as! User
newUser.createdTime = NSDate()
managedObjectContext.save(nil)
var i = 0
println("before array: clubsArray is \(clubsArray.count) clubs long")
var clubs = NSMutableSet.mutableSetValueForKey("clubs")
for newClub in clubsArray {
var club : Club = NSEntityDescription.insertNewObjectForEntityForName("Club", inManagedObjectContext: self.managedObjectContext) as! Club
club.type = clubsArray[i].type as String
club.estimatedMinYardage = clubsArray[i].minDistance as Int
club.estimatedMaxYardage = clubsArray[i].maxDistance as Int
club.lowerBound = clubsArray[i].lowerBound as Int
club.upperBound = clubsArray[i].upperBound as Int
//userClubs.addObject(club)
managedObjectContext.save(nil)
//club.setValue(newUser, forKey: "user")
println("\(club.type)")
i++
}
//user.setValue(userClubs, forKey: "clubs")
prepareUser()
}
}
}
Here's the console output from the first run:
No user yet
before array: clubsArray is 17 clubs long
Putter
LW
SW
PW
9i
8i
7i
6i
5i
5W
... [the rest of the clubs]
First user: Your Name
test: 7i
And from the second run after closing and reopening the app:
First user: Your Name
Bloody hell. I was trying to set 1 of the 17 instances of the Club class to a value that was less than it's minimum value allowed in the data model. If anyone else is having a similar issue check your min/max and default in addition to what #Tom-Harrington suggested in the comments of the original question. Dunno if Apple could get some kind of warning for this kind of thing into future versions of XCode.