this is my Model
public struct Welcome: Decodable{
public let userslist: [Userslist]
}
public struct Userslist: Decodable, Hashable{
public let full_name: String
public let partner_media: [PartnerMedia]
public init( partner_media: [PartnerMedia]) {
self.partner_media = partner_media
}
}
public struct PartnerMedia: Decodable , Hashable{
public var id = UUID()
public let url: String
public init( url: String) {
self.url = url
}
}
This is View Model I follow the MVVM pattern for accessing the data from API.
class PublisherModelVM: ObservableObject {
#Published var datas = [PartnerMedia]()
let url = "APIUrl"
init() {
getData(url: url)
}
func getData(url: String) {
guard let url = URL(string: url) else { return }
URLSession.shared.dataTask(with: url) { (data, _, _) in
if let data = data {
do {
let results = try JSONDecoder().decode(Welcome.self, from: data)
DispatchQueue.main.async {
self.datas = results.userslist `//Cannot assign value of type '[Userslist]' to type '[PartnerMedia]' what should I write for getting proper response`
}
}
catch {
print(error)
}
}
}.resume()
}
}
I want to fetch the url and full_name And set to the View
struct PublisherListView: View{
#StateObject var list = PublisherModelVM()
var body: some View{
ScrollView(.horizontal,showsIndicators: false){
ForEach(list.datas, id: \.id){ item in
Text(item.full_name)
AsyncImage(url: URL(string: item.url)){image in
image
.resizable()
.frame(width: 235, height: 125).cornerRadius(8)
}placeholder: {
Image(systemName: "eye") .resizable()
.frame(width: 235, height: 125).cornerRadius(8)
}
}
}
}
}
this Error show in my Xcode Cannot assign value of type '[Userslist]'
to type '[PartnerMedia]'
Please help me.
can anyone help me for recommending for API related full detailed
courses and thank you in advance
As I said before (in the questions you have deleted) pay attention to the details of your models to match the json data. Try this approach, works very well for me:
struct ContentView: View {
var body: some View {
PublisherListView()
}
}
struct ServerResponse: Decodable {
let userslist: [User]
}
struct User: Decodable, Identifiable {
let id: Int
let totalBooks: Int
let totalfollowers: Int
let fullAddress: String?
let partnerMedia: [PartnerMedia]
enum CodingKeys: String, CodingKey {
case id, totalBooks,totalfollowers
case partnerMedia = "partner_media"
case fullAddress = "full_address"
}
}
struct PartnerMedia: Decodable, Identifiable {
let id: Int
let url: String
}
struct PublisherListView: View{
#StateObject var list = PublisherModelVM()
var body: some View{
ScrollView(.horizontal,showsIndicators: false){
HStack(spacing:15) {
ForEach(list.datas, id: \.id){ item in
AsyncImage(url: URL(string: item.url)){ image in
image
.resizable()
.frame(width: 235, height: 125).cornerRadius(8)
} placeholder: {
Image(systemName: "eye") .resizable()
.frame(width: 235, height: 125).cornerRadius(8)
}
}
}
}
}
}
class PublisherModelVM: ObservableObject {
#Published var datas = [PartnerMedia]()
let url = "https://alibrary.in/api/publisherList"
init() {
getData(url: url)
}
func getData(url: String) {
guard let url = URL(string: url) else { return }
URLSession.shared.dataTask(with: url) { (data, _, _) in
if let data = data {
do {
let results = try JSONDecoder().decode(ServerResponse.self, from: data)
DispatchQueue.main.async {
for user in results.userslist {
self.datas.append(contentsOf: user.partnerMedia)
}
}
}
catch {
print(error)
}
}
}.resume()
}
}
How to delete and update structure type array in Core Data iOS Sswift? I am saving to core data like this. I need to delete and update selected cell containing values
let projectsInfo = NSEntityDescription.insertNewObject(forEntityName:"ItemsInfo", into: delegate.persistentContainer.viewContext) as! ItemsInfo
let auditArray:[String:[lendingData]] = ["allcreditData":SaveWitnessData.shared.LendingDataArray]
let jsonData = try! JSONEncoder().encode(auditArray) projectsInfo.values = jsonData
delegate.saveContext()
And my struct is like
struct lendingData : Codable {
let userName : String
let amount : String
let date : String
let type : String
var witnessDetails : [witnessData]
}
Option 1.
Using a class and NSSecureCoding is the best way of doing this. The most flexible.
///To See the whole thing in action you have to follow a few steps
///Step 1. Create an new SwiftUI project with CoreData
///Step 2. Copy all the code in Option 1 into a `.swift` file
///Step 3. Go to the `Persistence.swift` file
/// Place these 2 lines
/// `WitnessDataTransformer.register()`
/// `LendingDataTransformer.register()`
/// Just under `container = NSPersistentCloudKitContainer(name: "YourAppName")
///Step 4. Go to the CoreData model
/// Select the `Item` Entity
/// Add a `lendingData` attribute of type `Transformable`
/// Update the `Transformer` and `Custom Class` in the `Data Model Inspector` as shown
///Step 5. You should see the View on Canvas in this point
Photo for Step 4
Code
import SwiftUI
//struct and class should start with an uppercase
//You need secureCoding not codable
//You have to change to class because NSSecurecoding is not available for a struct -https://developer.apple.com/documentation/foundation/nssecurecoding
public class LendingData : NSObject, Identifiable, ObservableObject{
public let id: String
#Published var userName : String
#Published var amount : String
#Published var date : String
#Published var type : String
//WitnessData needs to conform to secure coding as well
#Published var witnessDetails : [WitnessData]
static func sample() -> LendingData {
LendingData(id: UUID().uuidString, userName: "sample name", amount: "10.00", date: "\(Date())", type: "sample type", witnessDetails: [WitnessData.sample(), WitnessData.sample()])
}
static func blank() -> LendingData {
LendingData(id: UUID().uuidString, userName: "", amount: "", date: "", type: "", witnessDetails: [])
}
public enum CodingKeys: String, CodingKey {
case id
case userName
case amount
case date
case type
case witnessDetails
}
public init(id: String, userName : String, amount : String, date : String, type : String, witnessDetails : [WitnessData]) {
self.id = id
self.userName = userName
self.amount = amount
self.date = date
self.type = type
self.witnessDetails = witnessDetails
}
public required init?(coder: NSCoder) {
id = coder.decodeObject(forKey: CodingKeys.id.rawValue) as! String
userName = coder.decodeObject(forKey: CodingKeys.userName.rawValue) as! String
amount = coder.decodeObject(forKey: CodingKeys.amount.rawValue) as! String
date = coder.decodeObject(forKey: CodingKeys.date.rawValue) as! String
type = coder.decodeObject(forKey: CodingKeys.type.rawValue) as! String
witnessDetails = coder.decodeArrayOfObjects(ofClass: WitnessData.self, forKey: CodingKeys.witnessDetails.rawValue) ?? []
}
}
extension LendingData: NSSecureCoding{
public static var supportsSecureCoding: Bool{
return true
}
public func encode(with coder: NSCoder) {
coder.encode(id, forKey: CodingKeys.id.rawValue)
coder.encode(userName, forKey: CodingKeys.userName.rawValue)
coder.encode(amount, forKey: CodingKeys.amount.rawValue)
coder.encode(date, forKey: CodingKeys.date.rawValue)
coder.encode(type, forKey: CodingKeys.type.rawValue)
coder.encode(witnessDetails, forKey: CodingKeys.witnessDetails.rawValue)
}
}
///MUST CALL LendingDataTransformer.register() right after creating the Persistent Container before setup and loading store
#objc(LendingDataTransformer)
public final class LendingDataTransformer: NSSecureUnarchiveFromDataTransformer {
public static let name = NSValueTransformerName(rawValue: String(describing: LendingDataTransformer.self))
public override static var allowedTopLevelClasses: [AnyClass] {
return [LendingData.self, NSString.self, NSArray.self, WitnessData.self]
}
//Register before CoreData setup starts
#objc dynamic
public static func register() {
let transformer = LendingDataTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
//You have to change to class because NSSecurecoding is not available for a struct -https://developer.apple.com/documentation/foundation/nssecurecoding
public class WitnessData: NSObject, Identifiable, ObservableObject{
public let id: String
//This is just a sample since you did not provide the struct
//Add your variables to
// the class,
// the CodingKeys,
// init?(coder: NSCoder),
// encode(with coder: NSCoder), and
// init(id: String, name : String).
// Just follow the pattern.
#Published var name: String
static func sample() -> WitnessData{
WitnessData(id: UUID().uuidString, name: UUID().uuidString)
}
static func blank() -> WitnessData{
WitnessData(id: UUID().uuidString, name: "")
}
public enum CodingKeys: String, CodingKey {
case id
case name
}
public init(id: String, name : String) {
self.id = id
self.name = name
}
public required init?(coder: NSCoder) {
id = coder.decodeObject(forKey: CodingKeys.id.rawValue) as? String ?? ""
name = coder.decodeObject(forKey: CodingKeys.name.rawValue) as? String ?? ""
}
}
extension WitnessData: 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)
}
}
///MUST CALL WitnessDataTransformer.register() right after creating the Persistent Container before setup and loading store
#objc(WitnessDataTransformer)
public final class WitnessDataTransformer: NSSecureUnarchiveFromDataTransformer {
public static let name = NSValueTransformerName(rawValue: String(describing: WitnessDataTransformer.self))
public override static var allowedTopLevelClasses: [AnyClass] {
return [WitnessData.self, NSString.self, NSArray.self]
}
//Register before CoreData setup starts
#objc dynamic
public static func register() {
let transformer = WitnessDataTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
The below SwiftUI code works for both option 1 or option 2
///This is just a sample View
struct LendingDataView: View {
//You will need the original ObservableObject if you want to be able to show changes
//SwiftUI depends on being told that there are chagnes so it can reload Views
#ObservedObject var item: Item
var body: some View {
if item.lendingData != nil{
List{
TextField("username",text: $item.lendingData.bound.userName)
TextField("amount",text: $item.lendingData.bound.amount)
TextField("date",text: $item.lendingData.bound.date)
TextField("type",text: $item.lendingData.bound.type)
Section(content: {
ForEach($item.lendingData.bound.witnessDetails, content: { $witness in
HStack{
TextField("name",text: $witness.name)
Spacer()
//For deleting by object
Image(systemName: "trash")
.foregroundColor(.red)
.onTapGesture {
let idx = item.lendingData!.witnessDetails.firstIndex(where: {
$0.id == witness.id
})
if idx != nil{
item.lendingData!.witnessDetails.remove(at: idx!)
}
//Because you are so far down the line you have to tell the ObservableObject there is a change
//If you dont you won't see the new items until something happens to trigger a refresh
//item.objectWillChange.send()
item.objectWillChange.send()
}
}
})
//For deleting by index
.onDelete(perform: { indexSet in
for idx in indexSet{
item.lendingData!.witnessDetails.remove(at: idx)
}
})
}, header: {
HStack{
Text("Witness Data")
Button(action: {
item.lendingData!.witnessDetails.append(WitnessData.blank())
//Because you are so far down the line you have to tell the ObservableObject there is a change
//If you dont you won't see the new items until something happens to trigger a refresh
item.objectWillChange.send()
}, label: {
Image(systemName: "plus")
})
}
})
}
}else{
VStack{
Text("no lending data")
Button(action: {
item.lendingData = LendingData.blank()
}, label: {
Image(systemName: "plus")
})
}
}
}
}
//Standard Preview
struct LendingDataView_Previews: PreviewProvider {
//Use the preview container
static let context = PersistenceController.preview.container.viewContext
static var sampleItem = Item(context: context)
static var previews: some View {
LendingDataView(item: sampleItem)
}
}
extension Optional where Wrapped == LendingData {
var _bound: LendingData? {
get {
return self
}
set {
self = newValue
}
}
var bound: LendingData {
get {
return _bound ?? LendingData.blank()
}
set {
_bound = newValue
}
}
}
Like I said at the start class is the safest way but you can use the struct.
Option 2
Just add an an attribute named lendingDataJSON of Type String? INSTEAD of the lendingData of type Transformable
struct LendingData : Codable, Identifiable{
let id: String
var userName : String
var amount : String
var date : String
var type : String
var witnessDetails : [WitnessData]
static func sample() -> LendingData {
LendingData(id: UUID().uuidString, userName: "sample name", amount: "10.00", date: "\(Date())", type: "sample type", witnessDetails: [WitnessData.sample(), WitnessData.sample()])
}
static func blank() -> LendingData {
LendingData(id: UUID().uuidString, userName: "", amount: "", date: "", type: "", witnessDetails: [])
}
}
struct WitnessData: Codable, Identifiable{
let id: String
var name: String
static func sample() -> WitnessData{
WitnessData( id: UUID().uuidString, name: UUID().uuidString)
}
static func blank() -> WitnessData{
WitnessData( id: UUID().uuidString, name: "")
}
}
//The App's CoreData Model will need an attibute
// named lendingDataJSON of Type String
extension Item{
//This computed property should be the only way that the app alters the LendingData
//If you use the lendingDataJSON directly you can corrupt all of it
var lendingData: LendingData?{
get{
let decoder = JSONDecoder()
if let obj = try? decoder.decode(LendingData.self, from: self.lendingDataJSON?.data(using: .utf8) ?? Data()) {
return obj
}else{
return nil
}
}
set{
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
if let encoded = try? encoder.encode(newValue) {
self.lendingDataJSON = String(data: encoded, encoding: .utf8) ?? ""
}
}
}
}
All the View code will work the same with the class option or with the struct option
var array: Array<Int>? = nil
var arrayType: Any.Type = type(of: array)
print("type:\(arrayType)")
I got printed:
type:Optional<Array<Int>>
then, how can i get the type Int from arrayType?
You may not understand why I did this. The actual situation is:
I have a bean, like this:
protocol Initializable {
init()
}
class MyBean1: Initializable {
required init() {}
var property1: Int?
var property2: String?
}
class MyBean2: Initializable {
required init() {}
var beans: Array<MyBean1>?
}
I have json data like this:
{beans:[{"property1":1,"property2":"string1"},{"property1":2,"property2":"string2"}]}
I want automatic create object and set the values.
I use the Runtime(A Swift Runtime library for viewing type info, and the dynamic getting and setting of properties.) Framework to mirror and set properties.
I extend the TypeInfo:
extension TypeInfo {
func properties() -> [String] {
var properties: [String] = []
for property in self.properties {
properties.append(property.name)
}
return properties
}
}
I want create object from json string:
static func fromJson<Result: Initializable>(json: String, type: Result.Type) -> Result {
var result: Initializable = Result.init()
if let dictionary = Dictionary<String, Any>.from(json: json) {
if let info: TypeInfo = try? typeInfo(of: type(of: result)) {
let properties = info.properties()
for (key, value) in dictionary {
if properties.contains(key) {
if let property: PropertyInfo = try? info.property(named: key) {
print("\(key):\(property.type)")
}
}
}
}
}
return result as! Result
}
I got print:
beans:Optional<Array<MyBean1>>
the property.type type is Any.Type.
I want get the type MyBean1 and create MyBean1's object from property.type.
Now i use Bundle.main.classNamed to get the generic type.
TypeName got from substring
var typeName = "\(property.type)" // Optional<Array<MyBean1>>
if let range = typeName.range(of: "Optional<") {
typeName = typeName.replacingCharacters(in: range, with: "")
typeName = String(typeName[typeName.startIndex..<(typeName.index(before: typeName.endIndex))])
}
print("typeName:\(typeName)") // Array<MyBean1>
if let range = typeName.range(of: "Array<") {
typeName = typeName.replacingCharacters(in: range, with: "")
typeName = String(typeName[typeName.startIndex..<(typeName.index(before: typeName.endIndex))])
print("typeName:\(typeName)") // MyBean1
if let namespace = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String {
let clazz: AnyClass? = Bundle.main.classNamed("\(namespace).\(typeName)")
if let initableType = clazz as? Initializable.Type {
var beans = Array<Any>()
for item in array {
var bean = initableType.init()
// set bean properties
beans.append(bean)
}
}
}
}
I'm trying to filter theDataArray by theCaseDataArray where dataName in theDataArray matches the dataName in theCaseDataArray. As you can see by the code, each array is defined by its own struct. I've marked the line in the code where I need help. I've tried using a $0 and everything I can think of but no joy. I know there are a number of similar question on the forum but none see to contain an answer to this particular situation.
import UIKit
import SQLite
struct Data_On_Case
{
var dataID: Int64
var dataName: String
}
struct MyData
{
let dataID: Int64
var dataName: String
var dataType: String?
var dataDescription: String?
var pricePaid: Double?
var shipping: Double?
var datePurchased: String?
var dataDuration: String?
var manufacturer: String?
var venue: String?
var performanceLevel: String?
var dataNotes: String?
var dataStyle: String?
}
class Test_VC: UIViewController
{
private let crossRefTable = Table("CrossRefCase_Data")
private let crossRefRCaseID = Expression<Int64>("CaseID_CaseXRef")
private let crossRefDataID = Expression<Int64>("DataID_CaseXRef")
private var theDataArray = [MyData]()
private let theDataTable = Table("My_Data")
private let dataID = Expression<Int64>("DataID")
private let data_Name = Expression<String>("Data_Name")
private var searchBarShowing: Bool = false
private var isSearching: Bool = false
private var searchingArray = [MyData]()
private var theCurrentArray = [MyData]()
private var theCaseDataArray = [Data_On_Case]()
#IBOutlet weak var theSearchBar: UISearchBar!
override func viewDidLoad()
{
super.viewDidLoad()
}
}
extension Test_VC: UISearchBarDelegate
{
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int)
{
switch selectedScope
{
case 0:
searchingArray = theDataArray
isSearching.toggle()
case 1:
// Cannot convert value of type '((Data_On_Case) throws -> Bool) throws -> Bool' to expected argument type '(MyData) throws -> Bool'
// The line below is where I'm stuck. The error is shown above.
searchingArray = theDataArray.filter(theCaseDataArray.contains(where: ))
isSearching = true
self.theSearchBar.resignFirstResponder()
theSearchBar.showsScopeBar = true
default:
return
}
}
}
Use filter as below,
searchingArray = theDataArray.filter { data in
return self.theCaseDataArray.contains(where: { $0.dataName == data.dataName })
}
Hi i am able to get response from server for array list. But i dont know how to parse that response in object array [Feed] and send it on on completion handler. My Code is as follows:
class FeedsService {
private var feedsEndPoint: String = "https://jsonplaceholder.typicode.com/posts"
public func getFeeds(completion: ([Feed]) -> Void) {
Alamofire.request(feedsEndPoint, method: .get)
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseJSON{ response in
print("Response \(response.result.value)")
}
}
}
Feed Model is as follows:
class Feed {
private var title: String
private var body: String
init(title:String, body: String) {
self.title = title
self.body = body
}
public func getTitle() -> String {
return self.title
}
public func getBody() -> String {
return self.body;
}
}
I want to parse this as Feed Array and sent it on completion callback.
I am using Alamofire rest library for loading data from rest server.
You can try this:
class FeedsService {
private var feedsEndPoint: String = "https://jsonplaceholder.typicode.com/posts"
public func getFeeds(completion: #escaping ([Feed]) -> Void) {
Alamofire.request(feedsEndPoint, method: .get)
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseJSON{ response in
print("Response \(response.result.value)")
var feeds = [Feed]()
if let jsonArray = response.result.value as? [[String: Any]] {
for json in jsonArray {
if let feed = Feed(json: json) {
feeds.append(feed)
}
}
completion(feeds)
} else {
// handle error
}
}
}
}
Also, update your Feed class:
class Feed {
private var title: String
private var body: String
init(title:String, body: String) {
self.title = title
self.body = body
}
init?(json: [String: Any]) {
guard let title = json["title"] as? String,
let body = json["body"] as? String else {
return nil
}
self.title = title
self.body = body
}
public func getTitle() -> String {
return self.title
}
public func getBody() -> String {
return self.body;
}
}
Just change "title" and "body" to whatever is the appropriate key in your json response.
As El Tomato is pointing out that Feed init is not working, this is a test code that can be tried out in a playground:
let testFeedJson = ["title": "Test Title", "body" : "Test Body"]
if let testFeed = Feed(json: testFeedJson) {
print(testFeed.getTitle())
print(testFeed.getBody())
}