I have some code that pulls users based on what they type on a login view. I am trying to check if a specific field is blank (nil) with something along the following
for user in users{
if user["secretkey"] == nil {
query.getObjectInBackgroundWithId(user.objectId) {
However, when the column "secretkey" is in fact nil, tested with println(user["secretkey"], the if statement continues to its default.
Use the form:
for user in users {
if let secretKey = user["secretkey"] {
query.getObjectInBackgroundWithId(user.objectId) {
}
}
(you can also then use secretKey directly inside the if block if you wanted to)
Fixed it in my situation by declaring it as a variable first using the following.
let secretKeyParse = user["secretkey"] as String?
if secretKeyParse == nil {
Related
I am trying to implement a user registration system and I need to know whether a user id (generated randomly) has already been assigned to another user or not. To do so, I connect my Firebase database and use the observer() method to check to availability of the generated id.
However, since the Firebase database query runs asynchronously and I can only know the result once the query returns, I am not able to use the correct return value from within the calling method.
My approach here is
repeat {
id = generateUniqueId()
check availability
} while (id is not unique)
The implementation I have is
var id:String
var test = true
repeat {
id = generateId()
ref.child("\(databaseReferenceName)").observe(.value) { (snapshot) in
test = snapshot.hasChild("\(id)")
}
} while (test == true)
This loop keeps running even though the test variable is set to false by the hasChild() method.
How can I change the code so that I would be able to capture the right value of the test variable?
I am using Swift 4.1
Thanks
You don't say, but I'm assuming the observe() function you're using is asynchronous, and will return before the work is done. As PaulW11 says, you need to trigger the next round in the completion handler:
func observeChildren() {
var id:String
id = generateId()
ref.child("\(databaseReferenceName)").observe(.value) { (snapshot) in
if snapshot.hasChild("\(id)") {
observeChildren() //recursively call observeChildren again.
}
}
Since the process is asynchronous the loop will run until hit the first test = false , but i think you need this recursive way until find an available id
func checkID() {
id = generateId()
ref.child("\(databaseReferenceName)").observeSingleEvent(.value) { (snapshot) in
let test = snapshot.hasChild("\(id)")
if test {
print("id exists")
checkID() // check another one
}
else {
print("this is the one \(id)")
}
}
}
another thing is that it should be observeSingleEvent instead of observe
So I have created a model class where I am using Alamofire to get data from an API. I want to display that data in the table to view so I was thinking of appending that model into an array in the VC and call it in the custom cell.
But I want to append that model into the array only if a key value matches a particular string.
However when I am using a simple if statement in the VC its giving me a fatal error saying bad instruction.
Code
Calling the Model
var notificationModel: NotificationModel!
var notification = [NotificationModel]()
viewDidLoad
if notificationModel.type == "meeting" {
self.notification.append(notificationModel)
}
Model
Class NotificationModel
var _type: String!
var type: String {
if _type == nil {
_type = "Err"
}
return _type
}
func downloadData() {
...
}
}
Here you have force unwrap NotificationModel. And you are trying to access type of a nil value.
Please try with the following fix.
var notificationModel: NotificationModel!
var notification = [NotificationModel]()
if notificationModel?.type == "meeting" {
notification.append(notificationModel!)
}
Hope this resolves the issue.
I'm trying to understand some code in a project I'm working on. I have an array property of strings:
var names: [String]!
func findName(name: String?) -> [Name]? {
if name != nil {
return nameManager.namesForSearchString(name)?.filter({self.names.contains($0.name)})
} else {
return nameManager.allNames.filter({self.names.contains($0.name)}) //<-what get's returned here?
}
}
What I don't understand is if the name is nil, what happens when .contains is called, and with that, what happens when .filter gets called? This is implemented in a Favorites class, and I need to call this function to return all favorites if a button is tapped, so what would I pass to this function to ensure that all the contents of Names: [Name] are returned?
On a lower level, I want to understand how .contains and .filter work and what gets returned if nil is passed to them.
Another version of the same method from a different commit (that I also did not write) is this:
func findFavorites(name: String?) -> [Station]? {
if name != nil {
return nameManager.namesForSearchString(name)!.filter({contains(self.names, $0.objectId)})
} else {
return nameManager.allNames.filter({contains(self.names, $0.objectId)})
}
}
I don't want to post a non-answer, but I do want this to be properly formatted so I guess a comment won't do. This might help you understand what's going on, and what happens with filter/contains. If you have any more questions, let me know, and I'll answer the question. If I'm completely off-base, let me know as well!
// I don't know why this is implicitely unwrapped, as a nil in this Array crashes Playground execution
var localNames: [String!] = ["Troy", "Bob", "Donald"]
// I'm just modelling what I know about NameManager
struct NameManager {
var allNames = [Name(name: "Bob"), Name(name: "Liz"), Name(name: "Anastasia")]
}
// I also assume the `name` in Name is a non-optional.
struct Name {
var name: String = "some name"
}
var nameManager = NameManager()
func findName(name: String?) -> [Name]? {
// Case where `name` is non-nil is excluded for demonstration purposes
// I have expanded all the closure short-hands so we always see what we're doing.
let allNames = nameManager.allNames
// namesMatchingName is of type [Name], that we get by applying a filter.
// `filter` works on a predicate basis: it goes through each element, one at a time,
// and checks if it meets the "predicate", that is, a boolean
// condition that returns true or false. If it DOES meet the criteria, it will be included in
let namesMatchingName = allNames.filter { (currentName) -> Bool in
// Now we're inside the filter-predicate. What we do here is check if the `currentName`
// is in `localNames`.
let namesHasCurrentName = localNames.contains(currentName.name)
// If the name IS in `localNames` we return true to the filter,
// which means it will be included in the final array, `namesMatchingName`.
return namesHasCurrentName
}
// So now we have all the names that appear in both `nameManager.allNames` and `localNames`
return namesMatchingName
}
findName(nil) // returns [{name: "Bob"}]
I am making a NSDictionary to hold data for a tableview. The tableview is like a contacts list, with contacts separated by first name and sorted alphabetically, so I am making a dictionary to hold these contacts. The dictionary is defined as [String: [User]]() where User is my own object.
However, when I check to see if there are any entries for the first letter in a user's first name, I get the compile error: Could not find member init on my code.
Here is my code:
var users = CurrentAccount.friends
var organizedUsers = [String: [User]]()
func organizeFriendsByFirstName() {
for user in users {
if let xc = organizedUsers["\(user.firstName!.substringToIndex(1))"] {
} else {
organizedUsers["\(user.firstName!.substringToIndex(1))"] = [user]
}
}
}
You error not have anything to do with your NSDictionary, sometimes the Swift compiler it's not accurate with its compile errors, see this nice article Swift Compiler Diagnostics.
If you put outside the line user.firstName!.substringToIndex(1) this throws the compile error :
Cannot invoke 'substringToIndex' with an argument list of type '(Int)'.
And it is the cause of your error, you can use the advance function to make indexes in an String, change your function to the following:
func organizeFriendsByFirstName() {
for user in users {
if let xc = organizedUsers["\(user.firstName!.substringToIndex(advance(user.firstName!.startIndex, 1)))"] {
} else {
organizedUsers["\(user.firstName!.substringToIndex(advance(user.firstName!.startIndex, 1)))"] = [user]
}
}
}
I hope this help you.
I'm coding an app where a logged in user has got a couple of extra functionalities than a non logged in user. Basically, I have more or less 5 tabs. When I launch the app, the user immediately gets the login page. He can decide to skip it. If he skips it, there'll only be 3 tabs for him. If he logs in successfully, there'll be 5.
I already have my login page made. I just don't know how I can store a session if the user is logged in correctly, and only display a certain number of tabs if the user isn't. I come from PHP, I've just started learning Objective-C, so I'm looking for the same thing as $_SESSION in PHP, more or less.
So: if user logs in, store session, and show all the tabs. If he doesn't, only show a limited number of tabs.
How should I approach this?
In terms of storing the session, I assume username and password is enough.
You could store the username as you wish in NSUserDefaults or CoreData if you are using it. Storing a password is best using the keychain. SSKeychain makes it easy to do this.
[SSKeychain setPassword:password forService:myAppName account:userName]
You could store the fact they are logged in in-memory, but on app relaunch check by:
NSString *password = [SSKeychain passwordForService:myAppName account:userName];
if (password != nil)
{
// Logged in
}
If the user logs out, easy as deleting the password from the keychain by
[SSKeychain deletePasswordForService:myAppName account:userName]
Session handling is done automatically when you use NSURLConnection, so you can store the users data in a Sesssion on the server.
What you might be looking for is called a Singleton design pattern (some people reject it, but it can be very handy). What you do is create one object that is available everywhere in your code. In this object you for example store a BOOL that indicates whether the user has logged in or not. For example:
(I didn't run this, just to get the idea)
Mananger myManager* = [Manager sharedManager];
if(myManager.loggedIn){
//Show 5 tabs
}else{
//Show 3 Tabs
}
This code can be used in every class so you can always access your user's data. Manager would be a seperate class in this case that provides singleton functionality. Check out how to make one here: http://www.johnwordsworth.com/2010/04/iphone-code-snippet-the-singleton-pattern/
I'm gonna give you a comprehensive answer.
Don't use NSUserDefaults as session it's a bad solution
NSUserDefaults data is not encrypted, it may cause security issue.
Let's create a structured user class instead
When the user logged in, you will need to make sure you have access to user data throughout the app so you can get the data on any screen when you need it.
To achieve this, we need to make a great structure to organize this properly. Remember that current user and another users are both "user" so we will use the same class.
Create a class and name it "EDUser" (you can choose other name if you want).
This class will contain a user information (either current user or other user).
More than that, this class will have capability to log the user in.
Here's a picture of what the class might look like:
class EDUser {
var firstName: String
var lastName: String?
var birthDate: NSDate?
init(firstName: String, lastName: String?, birthDate: NSDate?) {
self.firstName = firstName
self.lastName = lastName
self.birthDate = birthDate
}
}
// MARK: - Accessor
extension EDUser {
class var currentUser: EDUser? {
get {
return loadCurrentUserFromDisk()
}
set {
saveCurrentUserToDiskWithUser(newValue)
}
}
}
// MARK: - Log in and out
extension EDUser {
class func loginWithUsername(username: String,
andPassword password: String,
callback: (EDUser?, NSError) -> Void) {
// Access the web API
var parameters = [
"username": username,
"password": password
]
YourNetworkingLibrary.request(.POST,
"https://api.yourwebsite.com/login",
parameters: parameters).responseJSON {
response in
if response.statusCode == .Success {
let user = EDUser(firstName: response["firstName"],
lastName: response["lastName"],
birthDate: NSDate.dateFromString(response["birthDate"]))
currentUser = user
callback(currentUser, nil)
} else {
callback(nil, yourError)
}
}
}
class func logout() {
deleteCurrentUserFromDisk()
}
}
// MARK: - Data
extension EDUser {
class private func saveCurrentUserToDiskWithUser(user: EDUser) {
// In this process, you encode the user to file and store it
}
class private func loadCurrentUserFromDisk() -> EDUser? {
// In this process, you get the file and decode that to EDUser object
// This function will return nil if the file is not exist
}
class private func deleteCurrentUserFromDisk() {
// This will delete the current user file from disk
}
}
// MARK: - Helper
extension NSDate {
class func dateFromString(string: String) -> NSDate {
// convert string into NSDate
}
}
Use Case
Now with everything in place, we can use it like this
Non-blocking logging in process
EDUser.loginWithUsername(username: "edward#domain.com",
password: "1234") {
user, error in
if error == nil {
// Login succeeded
} else {
// Login failed
}
}
Logging out
EDUser.logout()
Check whether the user is logged in
if EDUser.currentUser != nil {
// The user is logged in
} else {
// No user logged in
// Show the login screen here
}
Get current user data on any screen
if let currentUser = EDUser.currentUser {
// do something with current user data
}
Store other user as object
let user = EDUser(firstName: "Edward",
lastName: "Anthony",
birthDate: NSDate())