Iam new in Swift and i want to convert my struct into parameter to post it with Alamofire 4.
Please excuse my bad english.
My struct is in another Class:
import Foundation
class structUser: NSObject{
var myStructUser = [person]()
struct person {
var firstName : String
var lastName : String
var age: Int
init ( firstName : String, lastName : String, age : Int) {
self.firstName = firstName
self.lastName = lastName
self.age = age
}
}
override init(){
myStructUser.append(person(firstName: "John", lastName: "Doe", age: 11))
myStructUser.append(person(firstName: "Richard", lastName: "Brauer", age : 22))
myStructUser.append(person(firstName: "Merrideth", lastName: "Lind", age : 55))
}
}
now in the Main Class I want to post the Alamofire, but how can I convert only the first name and the age from the struct?
import UIKit
class ViewController: UIViewController {
let classStructUser = structUser()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
print(classStructUser.myStructUser)
}
func postJson(){
//need format [String : Any]
for item in classStructUser.myStructUser{
// var name = classStructUser.myStructUser.name
// var age = classStructUser.myStructUser.age
}
print(classStructUser.myStructUser)
/*here i need the Json in format:
{
"name":"John",
"age":11
}
{
"name":"Richard",
"age":22
}
{
"name":"Merrideth",
"age":55
}
an so on array.count
*/
}
}
another Question:
How can I access a variable in the struct(structUser) from VieControllerClass
thx for your help! And please explain the full solution, because i want to understand how ist works.
Thx!
class structUser: NSObject{
var myStructUser = [person]()
struct person {
var firstName : String
var lastName : String
var age: Int
init ( firstName : String, lastName : String, age : Int) {
self.firstName = firstName
self.lastName = lastName
self.age = age
}
static func jsonArray(array : [person]) -> String
{
return "[" + array.map {$0.jsonRepresentation}.joined(separator: ",") + "]"
}
var jsonRepresentation : String {
return "{\"name\":\"\(firstName)\",\"age\":\"\(age)\"}"
}
}
func jsonRepresentation() -> String {
return person.jsonArray(array: myStructUser)
}
override init(){
myStructUser.append(person(firstName: "John", lastName: "Doe", age: 11))
myStructUser.append(person(firstName: "Richard", lastName: "Brauer", age : 22))
myStructUser.append(person(firstName: "Merrideth", lastName: "Lind", age : 55))
}
}
And use it like this.
let jsonString = classStructUser.myStructUser.jsonRepresentation()
Related
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?
}
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.
I am learning swift on my own but I cannot manage to make this code run, I am trying to create a class "Inhabitant" inheriting from Person class with a new "Country" property, have 2 initializers:
- one that takes firstname and lastname and sets the country to an empty String
- one that takes firstname, lastname and country
and create a computed property description of String type that return a the firstname, lastname and country separated by a space
class Person {
var firstname: String
var lastname: String
var fullname: String {
return firstname + " " + lastname
}
init(firstname: String, lastname: String) {
self.firstname = firstname
self.lastname = lastname
}
}
class Inhabitant: Person {
var country: String? = nil
var description: String {
return firstname + " " + lastname + " " + country ?? ""
}
override init(firstname: String, lastname: String) {
super.init(firstname: <#T##String#>, lastname: <#T##String#>)
self.country = ""
}
init(firstname: String, lastname: String, Country: String) {
super.init(firstname: <#T##String#>, lastname: <#T##String#>)
}
}
func tst() {
let perso = Inhabitant(firstname: "toto", lastname: "bobo")
print(perso)
}
The code won't run:
Editor placeholder in source file
I would do something like this:
class Person {
var firstname: String
var lastname: String
var fullname: String {
return firstname + " " + lastname
}
init(firstname: String, lastname: String) {
self.firstname = firstname
self.lastname = lastname
}
}
class Inhabitant: Person {
var country: String? = nil
var description: String {
//1
return "\(firstname) \(lastname) \(country ?? "")"
}
override init(firstname: String, lastname: String) {
//2
super.init(firstname: firstname, lastname: lastname)
self.country = ""
}
init(firstname: String, lastname: String, country: String) {
super.init(firstname: firstname, lastname: lastname)
self.country = country
}
}
func tst() {
let perso = Inhabitant(firstname: "toto", lastname: "bobo")
let perso2 = Inhabitant(firstname: "toto", lastname: "bobo", country: "Belgium")
print(perso.fullname)
print(perso2.description)
}
tst()
Explanations:
String interpolation in swift are done with this syntax, cf swift programming language
When you override the initialiser, what you want is to pass the arguments you receive to your super initialiser.
I'm working in swift 4.0, i have model object in MutableArray,
NSPredicate not working.
//Here is code
//Model class:
class Modelclass: NSObject
{
var firstName:String!
var lastName:String!
}
// Viewcontroller doing predicate:
let ary = NSMutableArray()
let userModel = Modelclass()
userModel.firstName = "Arrya"
userModel.lastName = "stark"
ary.add(userModel)
let commitPredicate = NSPredicate(format: "firstName == %#", "Arrya")
let resultAry = ary.filtered(using: commitPredicate)
print(resultAry)
I solved the problem,
Solution 1:
//I just added below code in model class
extension Modelclass {
#objc override func value(forKey key: String) -> Any? {
switch key {
case "firstName":
return firstName
case "lastName":
return lastName
default:
return nil
}
}
}
Solution 2:
Add #objc before variable:
class Modelclass: NSObject
{
#objc var firstName:String!
#objc var lastName:String!
}
var is introduced for Mutable Content/Variables
In swift you can use filter alternative to NSPredicate
Try Like this:
//Model class:
class Modelclass
{
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
// Viewcontroller doing predicate:
var ary:[Modelclass] = []
let userModel = Modelclass(firstName: "Arrya", lastName: "stark")
ary.append(userModel)
let resultAry = ary.filter{ $0.firstName == "Arrya" }
print(resultAry)
I need to use struct in my custom framework, how can the following be done?
Here my framework:
MyFramework.framework
public class SomeClass : NSObject {
public struct myDetails {
public var firstName: String?
public var lastName: String?
public init(fName: String? LastName lName: String?) {
self.firstName = fName
self.lastName = lName
}//init end
}// struct end
public func fillStruct(inout myStruct: myDetails) {
myStruct.firstName = "John"
myStruct.lastName = "Devid"
}
}// class end
Here's my Application:
// ViewController.swift
import MyFramework
var refObj: SomeClass?
override func ViewDidLoad() {
refObj = SomeClass()
let tempStruct = refObj.myDetails(fName: nil LastName lName: nil) // I CANT ABLE ACCESS LIKE THIS.
refObj.fillStruct(&tempStruct)
}
What I want, is my application to send the data to the struct and my framework to fill the struct and finish the function.
How can this be achieved?