Object Mapping in swift - ios

Hello i'm beginner in Swift programming.
Im trying to make an app with login, which will take user and pass data, parse to JSON and send it to server.
I need to able that an user take only two values: user and pass from field, parse the whole object into JSON and send post request.
Before all that i have problem with making User struc for mapping: error is on the end of init " Return from initializer without initialising all sorted properties".
struct User: Mappable{
private var _username: String!
private var _password: String!
private var _firstname: String
private var _lastname: String
var username: String{
get{
return _username
}set(username){
self._username = username
}
}
var password: String{
get{
return _password
}set(password){
self._password = password
}
}
var firstname: String{
get{
return _firstname
}set(firstname){
self._firstname = firstname
}
}
var lastname: String{
get{
return _lastname
}set(lastname){
self._lastname = lastname
}
}
init(username: String, password: String){
self._username = username
self._password = password
}
init?(_ map: Map) {
}
mutating func mapping(map: Map) {
username <- map["USERNAME"]
password <- map["PASSWORD"]
firstname <- map["FIRST_NAME"]
lastname <- map["LAST_NAME"]
}
}
I know i have to learn so much, but right now don't have time, so must finish this. I'm trying to learn as much as it possible.
Thank you so much

All of your non-optional properties must be initialized prior to exiting your init methods. Setting them to empty strings (or whatever) should eliminate the error.
init(username: String, password: String){
self._username = username
self._password = password
self._firstname = ""
self._lastname = ""
}
init?(_ map: Map) {
self._username = ""
self._password = ""
self._firstname = ""
self._lastname = ""
}
Alternately you could make those properties optional.

Related

Vapor 4 authentication

Hey I'm having some problems with the login controllers.My code is:
func login(_ req: Request) throws -> EventLoopFuture<UserToken>{
let user = try req.auth.require(User.self)
let token = try user.generateToken()
return token.save(on: req.db).map { token }
}
But I don't really know that how the function work in postman.This is my usermodel :
import Foundation
import Fluent
import Vapor
import FluentPostgresDriver
final class User:Model,Content{
static let schema = "user"
#ID(key: .id)
var id:UUID?
#Field(key:"帳號")
var account:String
#Field(key: "密碼")
var password:String
init() {}
init(id: UUID?=nil, account:String, password:String){
self.id=id
self.account=account
self.password=password
}
}
extension User: ModelAuthenticatable {
// 要取帳號的欄位
static var usernameKey: KeyPath<User, Field<String>> = \User.$account
// 要取雜湊密碼的欄位
static var passwordHashKey: KeyPath<User, Field<String>> = \User.$password
// 驗證
func verify(password: String) throws -> Bool {
try Bcrypt.verify(password, created: self.password)
}
}
extension User {
struct Create: Content {
var account: String
var password: String
var confirmPassword: String // 確認密碼
}
}
extension User.Create: Validatable {
static func validations(_ validations: inout Validations) {
validations.add("account", as: String.self, is: .count(10...10))
// password需為8~16碼
validations.add("password", as: String.self, is: .count(8...16))
}
}
extension User {
func generateToken() throws -> UserToken {
// 產生一組新Token, 有效期限為一天
let calendar = Calendar(identifier: .gregorian)
let expiryDate = calendar.date(byAdding: .day, value: 1, to: Date())
return try UserToken(value: [UInt8].random(count: 16).base64, expireTime: expiryDate, userID: self.requireID())
}
}
And this is my usertoken:
import Foundation
import Vapor
import Fluent
final class UserToken: Content, Model {
static let schema: String = "user_tokens"
#ID(key: .id)
var id: UUID?
#Field(key: "value")
var value: String
// oken過期時間
#Field(key: "expireTime")
var expireTime: Date?
// 關聯到User
#Parent(key: "user_id")
var user: User
init() { }
init(id: UUID? = nil, value: String, expireTime: Date?, userID: User.IDValue) {
self.id = id
self.value = value
self.expireTime = expireTime
self.$user.id = userID
}
}
extension UserToken: ModelTokenAuthenticatable {
//Token的欄位
static var valueKey = \UserToken.$value
//要取對應的User欄位
static var userKey = \UserToken.$user
// 驗證,這裡只檢查是否過期
var isValid: Bool {
guard let expireTime = expireTime else { return false }
return expireTime > Date()
}
}
While I'm typing the value of "account","password" and "confirmPassword", but it kept telling me that "User not authenticated." ,which I've already have the value in my database.
enter image description here
And I'm sure that the password was right. Is there anything that I missed? I'm pretty new in vapor.
And I followed the article below: https://ken-60401.medium.com/vapor-4-authentication-server-side-swift-1f96b035a117
I think the tutorial linked uses HTTP Basic authentication for the login route and I'm guessing that's the case judging by the code shown (it would be good to show how you're registering the login route).
If that's the case then you need to send the username and password in the request as basic authentication credentials in the Authorization header. The value should be Basic <Credentials> where Credentials is username:password Base 64 encoded. However you can get Postman to do it for you

Why can i not access attributes from another class in swift?

Im making a userProfile class in swift which includes the following code:
class userProfile {
var firstname: String!
var username: String!
var lastname: String!
var uid: String!
init(uid: String, dictionary: Dictionary<String, AnyObject>) {
self.uid = uid
if let username = dictionary["username"]as? String {
self.username = username
}
if let firstname = dictionary["firstname"]as? String {
self.firstname = firstname
}
if let lastname = dictionary["lastname"]as? String {
self.lastname = lastname
}
}
}
and i am trying to access the the attributes in another class with the following code:
var user: userProfile? {
didSet {
let fullName = userProfile?.firstname
firstname.text = fullName
}
}
But when i do this i get the following error and i don't know why:
Type 'userProfile?' has no member 'firstname'
how di i fix this?
Here is a fix (you tried to access class, but should be instance) :
var user: userProfile? {
didSet {
let fullName = user?.firstname // << here !!
firstname.text = fullName // I can't say if this valid
}
}
To avoid such ambiguity you should follow a rule to name classes in UpperCase, ie. in you case UserProfile.

How to test if an `NSObject` has a property with arbitrary name that can be set?

I want to implement a generic populating of a model from a dictionary. I test if the model has a property using a following test, but the condition always fails:
if (self.responds(to:(NSSelectorFromString(keyName)))){
self.setValue(keyValue, forKey: key )
}
Here is an example code:
import UIKit
class myModel: NSObject {
var userName: String = ""
var phoneNumber: String = ""
init(dict: Dictionary<String, Any>) {
super.init()
for (key, value) in dict {
let keyName = key
let keyValue: String = String(describing: value)
print("key \(key) value \(value)")
if (self.responds(to:(NSSelectorFromString(keyName)))){
self.setValue(keyValue, forKey: key )
}
}
}
}
You are almost done. Add the following at top of your NSObject class - #objcMembers
Such as -
import UIKit
#objcMembers
class myModel: NSObject {
This works as you expect (tested in playgrounds):
import UIKit
class myModel: NSObject {
#objc var userName: String = ""
#objc var phoneNumber: String = ""
init(dict: Dictionary<String, Any>) {
super.init()
for (key, value) in dict {
let keyCapitalized = key.prefix(1).uppercased() + key.dropFirst()
let keyName = "set\(keyCapitalized):"
let keyValue: String = String(describing: value)
print("key \(key) (selector: '\(keyName))' value \(value)")
if self.responds(to: Selector(keyName)) {
self.setValue(keyValue, forKey: key)
}
}
}
}
let m = myModel(dict: ["userName" : "milan", "data" : "data"])
print(">>> \(m.userName)") // prints ">>> milan"
print(">>> \(m.phoneNumber)") // prints ">>> " (number was not provided, and data key was ignored)
Just two points.
First of all, you need to expose those properties to ObjectiveC for responds() to work - therefore I added #objc annotations on both properties.
Second of all, the proper selector syntax to test if you can set the property named userName is NOT Selector("userName"), but Selector("setUserName:") (you are testing a setter).
Dictionaries need to be given a type for their keys and values. What you have in your initialiser is too ambiguous.
Because the result of getting something out of a dictionary can be nil, you need to provide a default value which is what happens after ??
class MyModel: NSObject {
var userName: String = ""
var phoneNumber: String = ""
init(dict: [String : String]) {
self.userName = dict["userName"] ?? ""
self.phoneNumber = dict["phoneNumber"] ?? ""
super.init()
}
}
Below way you can set model values from a dictionary.
class MyModel {
var userName: String?
var phoneNumber: String?
init(dict: [String:Any]?) {
self.userName = dict?["username"] as? String
self.phoneNumber = dict?["phonenumber"] as? String
}
}
Example Usage :
let myModel = MyModel(dict: ["username":"test","phonenumber":"1234567890"])

Social network app gives error on a return line

I was finalizing my social media application and I am consistently having the same error regarding the "username" of my user during the launch process of the application (app is running, user is logged in, and the next view controller fails to come up and it crashes giving EXC_BAD_INSTRUCTION).
I was thinking it might be adata base problem as I had that with the the profile picture, however, the user name is in the database registered as a user with its email and password.
The code of the section the error is in:
import Foundation
import Firebase
import FirebaseDatabase
class Post {
private var _username: String!
private var _userImg: String!
private var _postImg: String!
private var _likes: Int!
private var _postKey: String!
private var _postRef: DatabaseReference!
var username: String
{
return _username
}
var userImg: String
{
return _userImg
}
var postImg: String {
get {
return _postImg
} set {
_postImg = newValue
}
}
var likes: Int {
return _likes
}
var postKey: String {
return _postKey
}
init(imgURl: String, likes: Int, username: String, userImg: String) {
_likes = likes
_postImg = imgURl
_username = username
_userImg = userImg
}
init(postKey: String, postData: Dictionary<String, AnyObject>){
_postKey = postKey
if let username = postData["username"] as? String {
_username = username
}
if let userImg = postData["userImg"] as? String {
_userImg = userImg
}
if let postImg = postData["imageUrl"] as? String{
_postImg = postImg
}
if let likes = postData["likes"] as? Int {
_likes = likes
}
_postRef = Database.database().reference().child("posts").child(_postKey)
}
func adjustLikes(addLikes: Bool) {
if addLikes {
_likes = likes + 1
} else {
_likes = likes - 1
}
_postRef.child("likes").setValue(_likes)
}
}
the line where the error occurs:
return _username
I am just really puzzled what the issue could be. I have looked at all the IBOutlets, as well as removing and adding new users. I would appreciate any help.
In the init(postKey:postData:) constructor it's not guaranteed that the _username property will be set. However, the public username property is of a non-optional type String. My assumption is that the username getter tries to forcefully unwrap a nil value.
Your username variable is a computed read only property which is returning value from variable _username:String!(Forced unwrapped value means can not be nil else crash ) .You need to be sure that your variable is not nil.
example when _username is not nil-:
class Foo{
var _username:String = "tushar"
var username: String
{
return _username
}
}
var object = Foo()
print(object.username)
Example when your variable can get nil value-:
class Foo{
var _username:String!
var username: String
{
print(_username)
return _username
}
}
var object = Foo()
print(object.username)
If variable has no value in it that's a crash

Access data of class in class

I have this classes:
class FacebookDB: Object {
dynamic var userName: String = ""
dynamic var profilePicture: String = ""
}
class TwitterDB: Object {
dynamic var userName: String = ""
dynamic var profilePicture: String = ""
}
class UserDB: Object {
dynamic var id = 0
override class func primaryKey() -> String {
return "id"
}
dynamic var userName: String = ""
dynamic var firstName: String = ""
dynamic var lastName: String = ""
dynamic var email: String = ""
dynamic var facebookAccount = FacebookDB?()
dynamic var twitterAccount = TwitterDB?()
}
After I put a value inside the userName
//user value come from facebook
let testUser = UserDB()
let realm = try! Realm()
testUser.FacebookAccount?.userName = user.userName
print(user.userName)
print(testUser.FacebookAccount?.userName)
Here is the output:
vivieng
nil
Why can't I print testUser.FacebookAccount?.userName?
BTW I can access every value from UserDB but none of FacebookDB nor TwitterDB, any idea?
Finally I found how to deal with this.
I need to create a value like that:
let facebookAccount = FacebookDB()
And then put everything I want inside:
facebookAccount.userName = user.userName
And finally copy the class inside the class:
testUser.FacebookAccount = facebookAccount
And voilà, that's work now!

Resources