perform segue to bring user to chat with post creator - ios

I try to create a button which should bring the normal user to the chatController with the creator of a post

There are a number of issues here, and really too many to address in a single answer but let me try to point you in the right direction.
Lets start with this
let user = job?.addedByUser
self.messagesController?.showChatControllerForUser(user)
so the user var is assigned from the Job Class
class Job {
var addedByUser: String!
Notice that addedByUser property is a string, therefor the userVar is a string.
However, you're passing it to a function which requires a user class, not a string.
func showChatControllerForUserCreator(_ user: User) {
Which then throws an error
Cannot convert value of type 'String?' to expected argument type
'User'
The way to fix it is to decide if a user is a String or something else. It would most likely be a UserClass that you create
class UserClass {
var uid = ""
var user_name = ""
}
at the same time you need to have your users stored in a users node
users
uid_0
user_name = "Henry"
uid_1
user_name = "Bud"
and in your Jobs class, store a reference to the user with it's uid.
This could get rather lengthy but the next step is, after you loaded your job, to then load the user from it's uid. The JobClass would be
class Job {
var addedByUid: String! //this will be the users Firebase uid
etc etc

Related

issue when appending the RLMArray, update RLMArray in RLMObject and link it with object in swift realm 3.0

want some method like which do update the existing followers, and if not exists do add it to DB and link them to user something like
GETS CRASH OVER append in write block, due to duplicate primary key,
also, it works perfectly if no followers has been added in HKUser Table, once it comes to update it crashes
import UIKit
import RealmSwift
class HKUser: Object{
dynamic var full_name = ""
dynamic var email: String?
dynamic var user_id: String?
let followers = List<HKUser>()
override static func primaryKey() -> String? {
return "user_id"
}
}
I want to update the connection of a user in DB also, so I want to do some thing like
//1. updated the userFollower array with required data
let userFollowers:[HKUser] = []
//2. now need to link it with my user object and update it in db
if let user = realmWrapper.sharedInstance.getUser(forID: id) {
try! realm.write {
//want some method like which do update the existing followers,
//and if not exists do add it to db and link them to user something like
//realm.add(user, update: true)
user.followers.append(contentsOf: followers)
/**********
GETS CRASH OVER HERE,
due to duplicate primary key,
it works perfect if no followers has been added in HKUser Table,
once it comes to update it crashes
**********/
}
}
List<T>.append() method save to the Realm implicitly if the objects are unmanaged. That is why the duplicated primary key exception happens.
To avoid this, you can add or update the unmanaged objects before appending to the List. Then you append the objects to the List.
try! realm.write {
...
realm.add(followers, update: true) // Add or update the objects first
user.followers.append(contentsOf: followers)
...
}
Maybe it's not the case (not enough code in your question to tell) but it looks like you prepare the userFollowers array in step 1 to contain ALL the current followers for that specific user.
If this is the case then you'll end up re-adding all the existing followers not only the new ones, hence the duplicate keys.

Populate List in Swift 2.0 and display results

I've been learning iOS development for the past three weeks, I'm currently following a course on Udemy so far so good.
However I'm following one of the lectures whereby we build an Instagram Clone.
The instructor is using three arrays which are as follows:
var usernames = [""] // Stores all usernames
var userIds = [""] // Stores all Id's of the given usernames
var isFollowing = [false] // Stores where or not you're following that user
To me trying to keep track of what userId goes with what username using two arrays is basically an accident waiting to happen so I decided to set off and find a more feasible approach. I reverted back to my .Net days and decided to create a list so I went and created a class as follows:
class Users{
var Username : NSString = ""
var UserId : NSString = ""
var Following : Bool = false
}
Now inside my ViewController I make a call to Parse which returns me a list of users and I'm basically trying to loop through the response, and add them to the list class as shown here:
var t = [Users]() // After googling the web, this seems to be the syntax for a list declaration ?
let u = Users()
for object in users{
if let o = object as? PFUser {
u.Username = o.username!
u.UserId = o.objectId!
u.Following = o.IsFollowing!
self.t.append(u)
}
}
print(self.t)
Now when I print this to the console I see the following:
ParseStarterProject_Swift.Users
As I have one user at present, however when I try to loop through T and display the username in the console it doesn't display anything.
for x in t {
print(x.Username)
}
Your basic intuition is correct, it's better to have an array of custom objects, not multiple arrays.
Regarding making it more Swifty, consider your Users type. You might want something like:
struct User {
let username: String
let userId: String
let following: Bool
}
Note,
property names should start with lowercase letter;
Users should probably be called User, as it represents a single user;
we don't generally initialize values to default values like that, but rather specify them in the initializer;
we probably use String not NSString;
if a property cannot change, you'd use let, not var;
properties begin with lower case letters;
Then you can do something like:
var t = [User]()
for object in users {
if let o = object as? PFUser {
t.append(User(username: o.username!, userId: o.objectId!, following: o.IsFollowing!)
}
}
print(t)
Clearly, with all of those ! forced unwrapping operators, you'd want to be confident that those fields were populated for all of those properties.
Using struct is nice because (a) it's a value type; (b) you get the initializer for free; and (c) you can just print them. If you really wanted User to be a reference type (a class), you'd do something like:
class User {
let username: String
let userId: String
let following: Bool
init(username: String, userId: String, following: Bool) {
self.username = username
self.userId = userId
self.following = following
}
}
And if you wanted to be able to just print them, you'd define it to conform to CustomStringConvertible:
extension User: CustomStringConvertible {
var description: String { return "<User; username = \(username); userId = \(userId); following = \(following)>" }
}
With the class, you can feel free to change that description computed property to show it in whatever format you want, but it illustrates the idea.
You are correct in considering that keeping track of what userId goes with what username using two arrays is dangerous, you in the correct direction with your approach.
First, I would just like to suggest that you use correct naming convention:
Classes should be singular (except in very specific cases).
Variable/property names should begin with lowercase.
This would mean that your user class should look like this:
class User {
var username : NSString = ""
var userId : NSString = ""
var following : Bool = false
}
I will keep your existing naming use for the next part. The main problem with your code is that the variable "u" is a object which you create only once and then modify it. You should be creating a new "Users" object for each user instead of modifying the original. If you don't do this you will just have an array with the same user multiple times. This is how your code would look now:
var t = [Users]()
for object in users {
if let o = object as? PFUser {
let u = Users()
u.Username = o.username!
u.UserId = o.objectId!
u.Following = o.IsFollowing!
self.t.append(u)
}
}
print(self.t)
Next you mention that when you print to console you see the text: ParseStarterProject_Swift.Users, that is because Swift does not automatically print a pretty text with the content of your object. In order for it to print something more detailed, your "Users" object would need to implement the CustomStringConvertible. You can see a more detailed answer about that here: how-can-i-change-the-textual-representation-displayed-for-a-type-in-swif.
Lastly, you mention that when you loop trough "t" and display the username in the console it does not display anything. This is caused by one of two things:
Because there are no users being returned from parse, so the "t" array is actually empty. Try print(t.count) to see how many objects are in the array.
Because your "Users" object declares an empty string "" as the default username and the username is not being set correctly when getting the data from the parse. Which means that it IS actually printing something, just that it is an empty string. Try defining a different default value like var username : NSString = "Undefined" to see if it prints something.
Good luck learning swift!

send variable id to api to get user [duplicate]

I want to get back data in "requestActive" in "User" on Parse.t
I try this :
var user = PFUser()
var requestActive = user["requestActive"]
but : error "instance member 'user' cannot be used on type 'viewController'
You are declaring user at the top level of your view controller.
But then, still at the root level, you try to use it with user["requestActive"]: you can't do that.
You have to use user in a method, for example viewDidLoad.
So let's say the type of user["requestActive"] would be a String (just for the example), you would do something like:
var user = PFUser()
var requestActive: String?
override func viewDidLoad() {
super.viewDidLoad()
requestActive = user["requestActive"]
}
Of course, you have to replace String with your actual type.

Should I use optional for properties of object models that will be parsed from JSON?

My iOS app has a pretty common setup: it makes HTTP queries to an API server that responds with JSON objects. These JSON objects are then parsed to appropriate Swift objects.
Initially I divided properties into required properties and optional properties, mostly based on my API server's database requirements. For example, id, email, and name are require fields so they use non-optional types. Others can be NULL in database, so they are optional types.
class User {
let id: Int
let email: String
let profile: String?
let name: String
let motive: String?
let address: String?
let profilePhotoUrl: String?
}
Recently, I started wondering whether this was a good setup at all. I found out that although some properties might be always in the database, that does not mean that those properties will always be included in the JSON response.
For example, in the User profile page, all these fields are needed to properly display the view. Therefore, JSON response will include all these fields. For a view that lists users' names, however, I would not need email or id, and JSON response should probably not include those properties either. Unfortunately, this will cause error and crash the app when parsing JSON response into Swift object since the app expects id, email, name to be always not-nil.
I'm thinking of changing all properties of Swift objects into optionals, but it feels like throwing away all the benefits of this language-specific feature. Moreover, I will have to write many more lines of code to unwrap all these optionals somewhere else in the app anyway.
On the other hand, JSON objects are by their nature not very interoperable with strict static typing and nil-checking of Swift so it might be better to simply accept that annoyance.
Should I transition to models with every property as optionals? Or is there a better way? I'd appreciate any comment here.
There are three ways you can go with this:
Always send all the JSON data, and leave your properties non-optional.
Make all the properties optional.
Make all the properties non-optional, and write your own init(from:) method to assign default values to missing values, as described in this answer.
All of these should work; which one is "best" is opinion-based, and thus out of the scope of a Stack Overflow answer. Choose whichever one is most convenient for your particular need.
The first thing to do is ask: Does an element of the “view that lists users' names” need to be the same kind of object as the model object behind a “User profile page”? Perhaps not. Maybe you should create a model specifically for the user list:
struct UserList: Decodable {
struct Item: Decodable {
var id: Int
var name: String
}
var items: [Item]
}
(Although the question said the JSON response might not include id, it doesn't seem like a user list without ids with be particularly useful, so I made it required here.)
If you really want them to be the same kind of object, then maybe you want to model a user as having core properties that the server always sends, and a “details” field that might be nil:
class User: Decodable {
let id: Int
let name: String
let details: Details?
struct Details: Decodable {
var email: String
var profile: String?
var motive: String?
var address: String?
var profilePhotoUrl: String?
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
details = container.contains(.email) ? try Details(from: decoder) : nil
}
enum CodingKeys: String, CodingKey {
case id
case name
case email // Used to detect presence of Details
}
}
Note that I create the Details, if it's present, using Details(from: decoder), instead of the usual container.decode(Details.self, forKey: .details). I do it using Details(from: decoder) so that the properties of the Details come out of the same JSON object as the properties of the User, instead of requiring a nested object.
The premise:
Partial representing is a common pattern in REST. Does that mean all
properties in Swift need to be optionals? For example, the client
might just need a list of user ids for a view. Does that mean that all
the other properties (name, email, etc) need to be marked as optional?
Is this good practice in Swift?
Marking properties optional in a model only indicates that the key may or may not come. It allows the reader to know certain things about the model in the first look itself.
If you maintain only one common model for different API response structures and make all the properties optional, whether that's good practice or not is very debatable.
I have done this and it bites. Sometimes it's fine, sometimes it's just not clear enough.
Keeping one model for multiple APIs is like designing one ViewController with many UI elements and depending on particular cases, determining what UI element should be shown or not.
This increases the learning curve for new developers as there's more understanding-the-system involved.
My 2 cents on this:
Assuming we are going ahead with Swift's Codable for encoding/decoding models, I would break it up into separate models rather than maintaining a common model with all optionals &/or default values.
Reasons for my decision are:
Clarity of Separation
Each model for a specific purpose
Scope of cleaner custom decoders
Useful when the json structure needs a little pre-processing
Consideration of API specific additional keys that might come later on.
What if this User list API is the only one requiring more keys like, say, number of friends or some other statistic?
Should I continue to load a single model to support different cases with additional keys that come in only one API response but not another?
What if a 3rd API is designed to get user information but this time with a slightly different purpose? Should I over-load the same model with yet more keys?
With a single model, as the project continues to progress, things could get messy as key availability in now very API-case-based. With all being optionals we will have alot of optional bindings & maybe some shortcut nil coalescings here and there which we could have avoided with dedicated models in the first place.
Writing up a model is cheap but maintaining cases is not.
However, if I was lazy and I have a strong feeling crazy changes aren't coming up ahead, I would just go ahead making all the keys optionals and bear the associated costs.
I typically make all non-critical properties optional, and then have a failable initializer. This allows me to better handle any changes in the JSON format or broken API contracts.
For example:
class User {
let id: Int
let email: String
var profile: String?
var name: String?
var motive: String?
var address: String?
var profilePhotoUrl: String?
}
This means that I will never have a user object without an id or email (let's assume those are the two that always need to be associated with a user). If I get a JSON payload without an id or email, the Initializer in the User class will fail and won't create the user object. I then have error handling for failed initializers.
I'd much rather have a swift class with optional properties than a bunch of properties with an empty string value.
This really depends on the way you are handling your data. If you are handling your data through a "Codable" class, then you have to write a custom decoder to throw an exception when you don't get certain expected values. Like so:
class User: Codable {
let id: Int
let email: String
let profile: String?
let name: String
let motive: String?
let address: String?
let profilePhotoUrl: String?
//other methods (such as init, encoder, and decoder) need to be added below.
}
Because I know that I'm going to need to return an error if I don't get the minimum required parameters, you would need something like an Error enum:
enum UserCodableError: Error {
case missingNeededParameters
//and so on with more cases
}
You should be using coding keys to keep things consistent from the server. A way to do that inside of the User Object would be like so:
fileprivate enum CodingKeys: String, CodingKey {
case id = "YOUR JSON SERVER KEYS GO HERE"
case email
case profile
case name
case motive
case address
case profilePhotoUrl
}
Then, you need to write your Decoder. A way to do that would be like so:
required init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
guard let id = try? values.decode(Int.self, forKey: .id), let email = try? values.decode(String.self, forKey: .email), let name = try? values.decode(String.self, forKey: .name) else {
throw UserCodableError.missingNeededParameters
}
self.id = id
self.email = email
self.name = name
//now simply try to decode the optionals
self.profile = try? values.decode(String.self, forKey: .profile)
self.motive = try? values.decode(String.self, forKey: .motive)
self.address = try? values.decode(String.self, forKey: .address)
self.profilePhotoUrl = try? values.decode(String.self, forKey: .profilePhotoUrl)
}
SOMETHING TO NOTE: You should write your own encoder as well to stay consistent.
All of that can go, simply to a nice calling statement like this:
if let user = try? JSONDecoder().decode(User.self, from: jsonData) {
//do stuff with user
}
This is probably the safest, swift-ist, and most object oriented way to handle this type of issue.
I recommend to keep all non-scalar(String, Custom Types etc) properties as optional, scalar(Int, Float, Double etc) as non-optional(with some exceptions) by assigning a default value and collections with empty array. e.g,
class User {
var id: Int = 0
var name: String?
var friends: [User] = []
var settings: UserSettings?
}
This assures you a crash free app no matter what happens to server. I would prefer abnormal behavior over a crash.
If the server is giving Null value for the other properties, you can go for optionals and safe unwrap. Or while unwrapping you can assign empty string to property if the value is nil
profile = jsonValue ?? ""
Other case since the other properties are String data type you can assign default value as a empty string
class User {
let id: Int
let email: String
let profile: String = ""
let name: String
let motive: String = ""
let address: String = ""
let profilePhotoUrl: String = ""
}
Yes, you should use optional if the property is not necessary in API and if you want some value in the mandatory property then assign blank value:
class User {
let id: Int?
let email: String? = ""
let profile: String?
let name: String? = ""
let motive: String?
let address: String?
let profilePhotoUrl: String?
}
In my opinion, I will choose 1 of 2 solutions:
Edit my init func from JSON to object, init with default object values for all props (id = -1, email = ''), then read JSON with optional checking.
Create a new class/struct for that specific case.
I would prefer using failable Initializer its neat compared to other options.
So keep the required properties as non-optionals and create object only if they are present in the response (you can use if-let or gaurd-let to check this in response), else fail the creation of the object.
Using this approach we avoid making non-optionals as optionals and having a pain to handle them throughout the program.
Also optionals are not meant for defensive programming so don't abuse optionals by making "non-optional" properties as optionals.
I would prefer optional properties because you can not promise JSON values to be there all the time and any change on response property name would crash your app.
If you do not use optional values, you have to control parameters while parsing and add a default value if you want a crash free app. And you wouldn't know if it was nil or empty string from server.
Optional values is your best friends.
object mapper for mutable and non-mutable properties.
realm-swift for default non-optional values.

How to save toUser and fromUser in Parse?

New to Parse backend and coding all together.
Looking to create a "Favorite" function in my app so that users can save products that they like for later by tapping a simply UIButton.
I'm simply trying to figure out how to save the toUser (user whose product post is being favorited) and the fromUser (user who is doing the favoriting) in Parse.
Here is my code:
#IBAction func tagProductButton(sender: AnyObject) {
var favoritePost = PFObject(className: "Tag")
favoritePost["tagger"] = PFUser.currentUser()
favoritePost["productId"] = ??
favoritePost["userTagged"] = ??
favoritePost.saveInBackground()
}
"tagger" = toUser
"userTagged" = fromUser
Thank you in advance! remember, I've only been coding for about a month now
You should set the columns product and userTagged (from the Tag table) as Pointers if they are not already. Don't just use the ID as a String. You will need a reference to the Product (parse object) being tagged, so your code could look something like:
favoritePost["product"] = product
favoritePost["userTagged"] = product["user"]
You may not really need the userTagged field on the Tag table, since the user tagged is probably already attached to the product, but this may may other searches easier.

Resources