Getting response from AWS | Appsync - ios

I've Successfully Integrated the AWS-Client-App Sample couple of days ago then i started something from scratch. Let me give you my project specificaitons:-
Xcode version : Version 9.4 (9F1027a)
Language : Swift 4.0
Database : DynamoDB
And Using AppSync for syncing database
I Have a schema
type Mutation {
#------ User ------#
CreateUser(
FirstName: String,
Username: String,
Password: String,
CreatedOn: Int
): User
UpdateUser(UserId: ID!, FirstName: String!, Username: String!): User
DeleteUser(UserId: ID!): User
}
type Query {
####------ User ------#
GetUser(UserId: ID!): User
CheckUsername(Username: String): [User]
CheckPhoneNumber(Phone: String): [User]
#------ Login ------#
CheckUserLogin(Username: String, Password: String): [User]
}
type User {
UserId: ID!
FirstName: String
Username: String
#Password: String
Bio: String
Phone: String
Email: String
Address: String
WebsiteUrl: String
ProfilePicture: String
CreatedOn: Int
Updated: Int
Status: Boolean
LastLoggedIn: Int
IpAddress: String
}
schema {
query: Query
mutation: Mutation
}
A simple query in .graphql file
query CheckUsername($Username:String){
CheckUsername(Username:$Username)
{
Username
}
}
and generated a file called check Queries.swift
import AWSAppSync
public final class CheckUsernameQuery: GraphQLQuery {
public static let operationString =
"query CheckUsername($Username: String) {\n CheckUsername(Username: $Username) {\n __typename\n Username\n }\n}"
public var Username: String?
public init(Username: String? = nil) {
self.Username = Username
}
public var variables: GraphQLMap? {
return ["Username": Username]
}
public struct Data: GraphQLSelectionSet {
public static let possibleTypes = ["Query"]
public static let selections: [GraphQLSelection] = [
GraphQLField("CheckUsername", arguments: ["Username": GraphQLVariable("Username")], type: .list(.object(CheckUsername.selections))),
]
public var snapshot: Snapshot
public init(snapshot: Snapshot) {
self.snapshot = snapshot
}
public init(checkUsername: [CheckUsername?]? = nil) {
self.init(snapshot: ["__typename": "Query", "CheckUsername": checkUsername.flatMap { $0.map { $0.flatMap { $0.snapshot } } }])
}
public var checkUsername: [CheckUsername?]? {
get {
return (snapshot["CheckUsername"] as? [Snapshot?]).flatMap { $0.map { $0.flatMap { CheckUsername(snapshot: $0) } } }
}
set {
snapshot.updateValue(newValue.flatMap { $0.map { $0.flatMap { $0.snapshot } } }, forKey: "CheckUsername")
}
}
public struct CheckUsername: GraphQLSelectionSet {
public static let possibleTypes = ["User"]
public static let selections: [GraphQLSelection] = [
GraphQLField("__typename", type: .nonNull(.scalar(String.self))),
GraphQLField("Username", type: .scalar(String.self)),
]
public var snapshot: Snapshot
public init(snapshot: Snapshot) {
self.snapshot = snapshot
}
public init(username: String? = nil) {
self.init(snapshot: ["__typename": "User", "Username": username])
}
public var __typename: String {
get {
return snapshot["__typename"]! as! String
}
set {
snapshot.updateValue(newValue, forKey: "__typename")
}
}
public var username: String? {
get {
return snapshot["Username"] as? String
}
set {
snapshot.updateValue(newValue, forKey: "Username")
}
}
}
}
}
Then i called this block of code to run the query
let query = CheckUsernameQuery(Username:"testuser1")
AppSyncManager.Client().fetch(query: query, cachePolicy: .fetchIgnoringCacheData) {
(result, error) in
if error != nil {
print(error?.localizedDescription ?? "")
return
}
let username = result?.data?.checkUsername![0]?.username
}
And I'm Getting the following response
▿ Optional<GraphQLResult<Data>>
▿ some : GraphQLResult<Data>
- data : nil
- errors : nil
- source : AWSAppSync.GraphQLResult<AppName.CheckUsernameQuery.Data>.Source.server
- dependentKeys : nil
The query works well when i test it in Appsync Api panel but not working in the app. No Error, No Data nothing at all? What am i possibly doing wrong if any of you have an idea just feel free to let me know, it would be a great help for me. Thanks.

Related

How to map variable number of nested objects in object mapper swift

I have a JSON response (bellow) and I need to parse this -
[
{
"id":123,
"name":"Fahim Rahman",
"age":25,
"friends":[
{
"firstName": "Imtiaz",
"lastName": "Khan",
"avatar_url": null
}
],
"groups":{
"xcet":{
"name":"xcek cert etsh tnhg",
"createdDate":"2022-10-31T10:00:48Z"
},
"juyt":{
"name":"jfd uyt you to",
"createdDate":"2021-09-13T10:00:00Z"
},
"some random key":{
"name": "some name",
"createdDate":"2026-03-27T10:00:00Z"
}
}
}
]
To parse this in my code I've created this model. I can not able to parse the groups as that is not a list but an object -
import ObjectMapper
class Person: BaseObject {
#objc dynamic var ID: Int = -1
#objc dynamic var name: String = ""
#objc dynamic var age: Int = -1
var friendsList = List<Friends>()
override func mapping(map: ObjectMapper.Map) {
ID <- map["id"]
name <- map["name"]
age <- map["age"]
friendsList <- map["friends"]
}
}
class Friends: BaseObject {
#objc dynamic var firstName: String = ""
#objc dynamic var lastName: String = ""
#objc dynamic var avatarURL: String = ""
override func mapping(map: ObjectMapper.Map) {
firstName <- map["firstName"]
lastName <- map["name"]
avatarURL <- map["avatar_url"]
}
}
I know it's a bad JSON. The groups should be on the list instead of the nested objects but unfortunately, I'm getting this response.
Here in the response of groups, the number of nested objects is dynamic and the key of the nested object is also dynamic. Thus I can not able to parse this as friends attribute.
So my question is, how can I map the "groups"?
Before mapping groups, we need a class that can hold each Group alongside its key (i.e. xct)
For example
Class Groups: BaseObject {
#objc dynamic var key: String = ""
#objc dynamic var value: GroupsItem?
convenience init(key: String, value: GroupsItem) {
self.init()
self.key = key
self.value = value
}
}
Class GroupsItem: BaseObject {
#objc dynamic var name: String?
#objc dynamic var createdDate: String?
...
}
Then inside your Person class you can map this as -
private func mapGroupsItems(map: ObjectMapper.Map) -> List<GroupsItem> {
var rowsDictionary: [String: Groups]?
rowsDictionary <- map["groups"]
let rows = List<GroupsItem>()
if let dictionary = rowsDictionary {
for (key, value) in dictionary {
rows.append(GroupsItem(key: key, value: value))
}
}
return rows
}
dont forget to call this method from mapping -
override public func mapping(map: ObjectMapper.Map) {
...
groups = mapGroupsItems(map: map)
}
try this approach, using a custom init(from decoder: Decoder) for Groups, works well for me. Use a similar approach for non-SwiftUI systems.
struct ContentView: View {
#State var people: [Person] = []
var body: some View {
ForEach(people) { person in
Text(person.name)
ForEach(Array(person.groups.data.keys), id: \.self) { key in
Text(key).foregroundColor(.red)
Text(person.groups.data[key]?.name ?? "no name").foregroundColor(.blue)
Text(person.groups.data[key]?.createdDate ?? "no date").foregroundColor(.blue)
}
}
.onAppear {
let json = """
[
{
"id":123,
"name":"Fahim Rahman",
"age":25,
"friends":[
{
"firstName": "Imtiaz",
"lastName": "Khan",
"avatar_url": null
}
],
"groups":{
"xcet":{
"name":"xcek cert etsh tnhg",
"createdDate":"2022-10-31T10:00:48Z"
},
"juyt":{
"name":"jfd uyt you to",
"createdDate":"2021-09-13T10:00:00Z"
},
"some random key":{
"name": "some name",
"createdDate":"2026-03-27T10:00:00Z"
}
}
}
]
"""
if let data = json.data(using: .utf8) {
do {
self.people = try JSONDecoder().decode([Person].self, from: data)
print("---> people: \(people)")
} catch {
print("decode error: \(error)")
}
}
}
}
}
struct Person: Identifiable, Codable {
let id: Int
var name: String
var age: Int
var friends: [Friend]
var groups: Groups
}
struct Friend: Codable {
var firstName, lastName: String
var avatarURL: String?
enum CodingKeys: String, CodingKey {
case firstName, lastName
case avatarURL = "avatar_url"
}
}
struct Info: Codable {
var name: String
var createdDate: String
}
struct Groups: Identifiable, Codable {
let id = UUID()
var data: [String:Info] = [:]
init(from decoder: Decoder) throws {
do {
let container = try decoder.singleValueContainer()
self.data = try container.decode([String:Info].self)
} catch {
print(error)
}
}
}
Your Model classes structure will be
// MARK: - Welcome7Element
struct Welcome7Element {
let id: Int
let name: String
let age: Int
let friends: [Friend]
let groups: Groups
}
// MARK: - Friend
struct Friend {
let firstName, lastName: String
let avatarURL: NSNull
}
// MARK: - Groups
struct Groups {
let xcet, juyt, someRandomKey: Juyt
}
// MARK: - Juyt
struct Juyt {
let name: String
let createdDate: Date
}
Thank you #shakif_ for your insightful answer. Here is how I solved this based on that answer -
import ObjectMapper
import RealmSwift
class Person: BaseObject {
#objc dynamic var ID: Int = -1
#objc dynamic var name: String = ""
#objc dynamic var age: Int = -1
var friendsList = List<Friends>()
var groups = List<Group>
override func mapping(map: ObjectMapper.Map) {
ID <- map["id"]
name <- map["name"]
age <- map["age"]
friendsList <- map["friends"]
groups = extractGroups(map)
}
private func extractGroups(_ map: ObjectMapper.Map) -> List<Group> {
let items = List<Group>()
var modifiedJSON = [String: Group]
modifiedJSON <- map["groups"]
for (key,value) in modifiedJSON {
let item = GroupMapper(key: key, value: value)
if let group = item.value {
items.append(group)
}
}
return items
}
}
class Friends: BaseObject {
#objc dynamic var firstName: String = ""
#objc dynamic var lastName: String = ""
#objc dynamic var avatarURL: String = ""
override func mapping(map: ObjectMapper.Map) {
firstName <- map["firstName"]
lastName <- map["name"]
avatarURL <- map["avatar_url"]
}
}
class Group: BaseObject {
#objc dynamic var name: String = ""
#objc dynamic var createdDate: String = ""
override func mapping(map: ObjectMapper.Map) {
name <- map["name"]
createdDate <- map["createdDate"]
}
}
struct GroupMapper {
var key: String = ""
var value: Group?
}

NSKeyedUnarchiver error in Swift Core Data

I have this core data issue:
*** -[NSKeyedUnarchiver decodeObjectForKey:]: value for key (id) is not an object. This will become an error in the future.
First I would like to describe what I am trying to do:
I perform a GET request to my API, get an array of elements and add the data I got to Core Data.
Here is my CoreData Entity:
extension PetEntity {
#nonobjc public class func fetchRequest() -> NSFetchRequest<PetEntity> {
return NSFetchRequest<PetEntity>(entityName: "PetEntity")
}
#NSManaged public var category: CategoryDTO?
#NSManaged public var id: Int64
#NSManaged public var name: String?
#NSManaged public var photoUrls: [String]?
#NSManaged public var status: String?
#NSManaged public var tags: [TagDTO]?
}
extension PetEntity : Identifiable {
}
My DTO I use to get Json data in and then map it to my model:
struct PetDTO: Codable {
var id: Int
var category: CategoryDTO?
var name: String?
var photoUrls: [String]?
var tags: [TagDTO]?
var status: StatusDTO
}
public class CategoryDTO: NSObject, Codable {
var id: Int
var name: String?
public enum CodingKeys: String, CodingKey {
case id
case name
}
public required init?(coder: NSCoder) {
id = coder.decodeObject(forKey: CodingKeys.id.rawValue) as? Int ?? 0 // error here
name = coder.decodeObject(forKey: CodingKeys.name.rawValue) as? String
}
}
extension CategoryDTO: NSSecureCoding{
public static var supportsSecureCoding: Bool{
return true
}
public func encode(with coder: NSCoder) {
coder.encode(id, forKey: CodingKeys.id.rawValue)
coder.encode(name, forKey: CodingKeys.name.rawValue)
}
}
#objc(CategoryDtoTransformer)
public final class CategoryDtoTransformer: NSSecureUnarchiveFromDataTransformer {
public static let name = NSValueTransformerName(rawValue: String(describing: CategoryDtoTransformer.self))
public override static var allowedTopLevelClasses: [AnyClass] {
return [CategoryDTO.self, NSString.self, NSArray.self]
}
#objc dynamic
public static func register() {
let transformer = CategoryDtoTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
public class TagDTO: NSObject, Codable {
var id: Int
var name: String?
public enum CodingKeys: String, CodingKey {
case id
case name
}
public required init?(coder: NSCoder) {
id = coder.decodeObject(forKey: CodingKeys.id.rawValue) as? Int ?? 0 // Error here
name = coder.decodeObject(forKey: CodingKeys.name.rawValue) as? String
}
}
extension TagDTO: NSSecureCoding{
public static var supportsSecureCoding: Bool{
return true
}
public func encode(with coder: NSCoder) {
coder.encode(id, forKey: CodingKeys.id.rawValue)
coder.encode(name, forKey: CodingKeys.name.rawValue)
}
}
#objc(TagDtoTransformer)
public final class TagDtoTransformer: NSSecureUnarchiveFromDataTransformer {
public static let name = NSValueTransformerName(rawValue: String(describing: TagDtoTransformer.self))
public override static var allowedTopLevelClasses: [AnyClass] {
return [TagDTO.self, NSString.self, NSArray.self]
}
#objc dynamic
public static func register() {
let transformer = TagDtoTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
enum StatusDTO: String, Codable {
case available
case sold
case pending
}
And here is my CRUD code for adding to database:
class CoreDataPetPersistance: PetPerstitenceProtocol {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func add(pet: PetDTO) {
let newPet = PetEntity(context: self.context)
newPet.id = Int64(pet.id)
newPet.name = pet.name
newPet.category = pet.category
newPet.photoUrls = pet.photoUrls
newPet.tags = pet.tags
newPet.status = pet.status.rawValue
do {
try self.context.save()
} catch {
print(error)
}
}
}
Also I have added constraints to my ID to make each pet I get from API unique and registered CategoryDTO and TagDTO in app delegate. It should be fine.
Full error I get:
CoreData: error: -executeRequest: encountered exception = Error Domain=NSCocoaErrorDomain Code=133000 "Attempt to access an object not found in store." UserInfo={objectID=0xa57351074a65fcb3 <x-coredata://FEB02FEE-9B8B-4785-82A1-263F82D4CDBC/PetEntity/p1095>} with userInfo = {
NSCocoaErrorDomain = 133000;
NSUnderlyingError = "Error Domain=NSCocoaErrorDomain Code=133000 \"Attempt to access an object not found in store.\" UserInfo={objectID=0xa57351074a65fcb3 <x-coredata://FEB02FEE-9B8B-4785-82A1-263F82D4CDBC/PetEntity/p1095>}";
}
Please help me fix this and explain why this error happens
Thanks
There are dedicated APIs for scalar types
id = coder.decodeInt64(forKey: CodingKeys.id.rawValue)
Have a look for all available APIs
To match the types declare id as Int64 or convert the type.

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

Call func with various struct

I want to create one func which i can used with various struct.
I have several struct and I want use one func with all my struct.
I work with Firestore and want use this one func to access the Firestore.
My first struct:
struct Profile {
var name = ""
var surname = ""
var email = ""
var dictionary: [String: Any] {
return [
"name": name,
"surname": surname,
"email": email
]
}
}
extension Profile: DocumentSerializable {
init?(dictionary: [String: Any], id: String) {
let name = dictionary["name"] as? String ?? ""
let surname = dictionary["surname"] as? String ?? ""
let email = dictionary["email"] as? String ?? ""
self.init(name: name,
surname: surname,
email: email)
}
}
My second struct:
struct FavoriteList {
var favoriteList: [String]
var id: String
var dictionary: [String: Any] {
return [
"favoriteList": favoriteList,
"id": id
]
}
}
extension FavoriteList: DocumentSerializable {
init?(dictionary: [String : Any], id: String) {
let favoriteList = dictionary["favorite"] as? [String] ?? [""]
let id = id
self.init(favoriteList: favoriteList, id: id)
}
}
And my func which I used now to load data from firestore:
func observeQuery() {
guard let query = query else { return }
let time = DispatchTime.now() + 0.5
listener = query.addSnapshotListener { [unowned self] (snapshot, error) in
if let snapshot = snapshot {
DispatchQueue.main.asyncAfter(deadline: time) {
let profileModels = snapshot.documents.map { (document) -> Profile in
if let profileModel = Profile(dictionary: document.data(), id: document.documentID) {
return profileModel
} else {
fatalError("Error!")
}
}
self.profile = profileModels
self.document = snapshot.documents
self.tableView.reloadData()
}
}
}
}
So how I can make func observeQuery to use my structs Profile or FavouriteList?
You can use Generic Functions :
func observeQuery<T>(someObject: T) {
if someObject is Profile {
//do something
} else if someObject is FavouriteList {
//do something
}
}

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

Resources