SwiftUI Share variable Between Struct - variables

import SwiftUI
struct ReserveView: View {
#State var searchT = ""
#State var isSearching = false
#State private var showCheckAlert = false
#Binding var roomnum:Int
#StateObject private var vm = ReserveViewModel(
service: ReserveService()
)
var body: some View {
VStack{
HStack{
TextField("Search", text:$searchT)
.padding(.leading, 30)
}
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(6)
.padding(.horizontal)
.onTapGesture(perform: {
isSearching = true
})
.overlay(
HStack {
Image(systemName: "magnifyingglass")
Spacer()
}.padding(.horizontal,32)
.foregroundColor(.white)
)
if isSearching {
Button(action:{
isSearching = false
searchT = ""
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for:nil)
}, label: {
Text("Cancle")
.padding(.trailing)
.padding(.leading,0)
})
.transition(.move(edge: .trailing))
}
switch vm.state{
case .success(let data):
List{
ForEach((data).filter({"\($0)".contains(searchT)||searchT.isEmpty}),
id: \.roomnum){ item in
HStack{
Text("\(item.when) \(item.time) \(item.username)").foregroundColor(Color.black)
}
}
}
.padding(.bottom,15)
//.padding(.top,20)
case .loading:
ProgressView()
default:
EmptyView()
}
}
.task {
await vm.getReserves()
}
}
}
struct ReserveView_Previews: PreviewProvider {
static var previews: some View {
ReserveView(roomnum:.constant(""))
}
}
import Foundation
import SwiftUI
struct ReserveService {
enum ReserveListError: Error {
case failed
case failedToDecode
case invalidStatusCode
}
func fetchReserves() async throws -> [Reserve] {
let url = URL(string: "https://f6d3-119-203-102/roomreserveview?roomnum=\(here i want use variable)")!
let configuration = URLSessionConfiguration.ephemeral
print(url)
let (data, response) = try await URLSession(configuration: configuration).data(from: url)
guard let response = response as? HTTPURLResponse,
response.statusCode == 200 else{
throw ReserveListError.invalidStatusCode
}
let decodedData = try JSONDecoder().decode(ReserveServiceResult.self, from: data)
return decodedData.reserveInfo
}
}
import SwiftUI
import Foundation
#MainActor
class ReserveViewModel: ObservableObject {
enum State {
case na
case loading
case success(data: [Reserve])
case failed(error: Error)
}
#Published private(set) var state: State = .na
#Published var hasError: Bool = false
private let service: ReserveService
init(service: ReserveService) {
self.service = service
}
func getReserves() async {
self.state = .loading
self.hasError = false
do {
let reserves = try await service.fetchReserves()
self.state = .success(data: reserves)
}catch {
self.state = .failed(error: error)
self.hasError = true
print(String(describing: error))
}
}
}
hello! I'd like to ask you a SwiftUI question.
Based on the ReserveService file, I am implementing the part that lists and displays the desired data in ReserveView.
I want to complete the url in the 'fetchReserves' function by receiving the variable 'roomnum' from the ReserveView model to the ReserveService.
However, Binding does not seem to work because ReserveService is not a view model. Is there any way I can get this variable from the viewmodel?
If you don't understand my explanation, please ask the question again.
This is my first time asking a question. Please forgive me if there is something missing in my question

It is possible to inject it as function argument, like
func fetchReserves(_ roomnum: Int) async throws -> [Reserve] {
let url = URL(string:
"https://f6d3-119-203-102/roomreserveview?roomnum=\(roomnum)")!

Related

POST method API with parameters in swiftUI

I am retrieving details from my api with POST method, I have included the parameters here. I am able to print the response in console., but to the view it's quite complicating. Here added the code which i've tried. I appreciate if someone help me to get this done.
My network code goes here:
class HostApi: ObservableObject {
#Published var todos = [HostsHome]()
#Published var amenity = [AmenitiesHome]()
func loadData() {
let Url = String(format: Host_home)
guard let serviceUrl = URL(string: Url) else {
return
}
let parameters: [String : Any] = [
"request" : ["email" : "xxxxxxxxxxx.com",
"starting" : 0,
"ending" : 10]
]
var request = URLRequest(url: serviceUrl)
request.httpMethod = "POST"
request.setValue("Application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) else {
return
}
request.httpBody = httpBody
request.timeoutInterval = 20
let session = URLSession.shared
session.dataTask(with: request) { data, response, error in
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: .json5Allowed)
print(json)
} catch {
print(error)
}
}
}.resume()
}
}
ContentView code goes here :
struct Hosts_Home: View {
#StateObject var viewModel = HostApi()
var body: some View {
ForEach(viewModel.todos, id: \.title) { todo in
Text(todo.title!)
}
.onAppear {
viewModel.loadData()
}
}
}
[![api[![parameters][1]][1]][2]
[1]: https://i.stack.imgur.com/R5sBV.png
[2]: https://i.stack.imgur.com/yMj0t.png
Found a solution, I changed my network class like this below and it worked.
class HostApi: ObservableObject {
#Published var todos = [HostsHome]()
#Published var amenity = [AmenitiesHome]()
func loadData() {
let url = URL(string: Host_home)
guard let requestUrl = url else { fatalError() }
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
let postString = "email=xxxxxxx#gmail.com&starting=0&ending=10";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
do {
if let todoData = data {
let decodedData = try JSONDecoder().decode([HostsHome].self, from: todoData)
DispatchQueue.main.async {
self.todos = decodedData
self.amenity = decodedData[0].amenities
print(decodedData[0].propertyTypeGroup)
}
} else {
print("No data")
}
} catch {
print(error)
}
}
task.resume()
}
}

async image in SwiftUI

I am working on an api, in which i retrieve the texts but the image from the api is not showing inside the view. I have given it an async image. The async image shows as a grey part in the view. Please let me know what is missing here. It would be great if someone would help me out with this.
API modal as:
struct priceRange: Codable {
let status: String
let record: Record
}
struct Record: Codable {
let propertytype: [Property]
let placetype: [Place]
let floorplan: [Floor]
let amenity: [Amenity]
let path: String
}
struct Property: Codable {
let type: String
let image: String
let status: String
let id: Int
}
My network code goes here:
class PRViewModel: ObservableObject {
#Published var floors = [Floor]()
#Published var place = [Place]()
#Published var prop = [Property]()
#Published var res = [Amenity]()
#Published private(set) var exp: priceRange?
#Published private(set) var rec: Record?
func loadData(){
guard let url = URL(string: PR_data) else {
print("Invalid URL")
return
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
URLSession.shared.dataTask(with: request) {(data, response, error) in
do {
if let todoData = data {
let decodedData = try JSONDecoder().decode(priceRange.self, from: todoData)
DispatchQueue.main.async {
self.res = decodedData.record.amenity
self.prop = decodedData.record.propertytype
self.floors = decodedData.record.floorplan
self.place = decodedData.record.placetype
print(decodedData.status)
//print(decodedData.record.path!)
}
} else {
print("No data")
}
} catch {
print(error)
}
}.resume()
}
}
List code goes here :
struct Price_range: View {
#StateObject var viewModel = PRViewModel()
var body: some View {
List(viewModel.prop, id: \.type) { item in
Text(item.type)
AsyncImage(url: URL(string: PR_URL + (viewModel.rec?.path ?? "") + "/" + item.image))
}
.onAppear {
viewModel.loadData()
}
}
}
Edit:
AsyncImage(url: URL(string: PR_URL + (viewModel.exp?.record.path ?? "") + "/" + item.image))
it still remains the same. I want to bring that “path” variable in the “record” modal to the view?
As allready pointed out in the comments you never assign any value to exp and res so they stay nil. You could assign them while you assign your previous properties:
do {
if let todoData = data {
let decodedData = try JSONDecoder().decode(priceRange.self, from: todoData)
DispatchQueue.main.async {
self.exp = decodedData // this
self.rec = decodedData.record // and this
self.res = decodedData.record.amenity
self.prop = decodedData.record.propertytype
self.floors = decodedData.record.floorplan
self.place = decodedData.record.placetype
print(decodedData.status)
//print(decodedData.record.path!)
}
} else {
print("No data")
}
} catch {
print(error)
}
and then do:
AsyncImage(url: URL(string: PR_URL + (viewModel.rec?.path ?? "") + "/" + item.image))
if there is still an issue try to verify your link is valid by using:
let _ = print(PR_URL + (viewModel.rec?.path ?? "") + "/" + item.image)
right before the line with the AsyncImage.

problem when authorize then how to define provider in swifter.authorize method

i am facing problem to give provider name because i dont know how to give proper provider in swifter.authorize
my controller is where i am using login code
you can check func actiontwitter in which i have used provider then please suggest me how to use provider as parameter
i have installed swifter package in project
//
// twitterVc.swift
// socialLogin
//
// Created by ios on 19/11/22.
//
import UIKit
import FirebaseAuth
import Swifter
import SafariServices
struct TwitterConstants {
static let CONSUMER_KEY = "MY_CONSUMER_KEY"
static let CONSUMER_SECRET_KEY = "MY_CONSUMER_SECRET_KEY"
static let CALLBACK_URL = "MY_CALLBACK_URL"
}
class twitterVc: UIViewController {
var swifter: Swifter!
var accToken: Credential.OAuthAccessToken?
#IBOutlet weak var submitBtn: UIButton!
var provider = OAuthProvider(providerID: "twitter.com")
override func viewDidLoad() {
super.viewDidLoad()
self.isLoggedIn { loggedin in
if loggedin {
// Show the ViewController with the logged in user
print("Logged In?: YES")
} else {
// Show the Home ViewController
print("Logged In?: NO")
}
}
}
func isLoggedIn(completion: #escaping (Bool) -> ()) {
let userDefaults = UserDefaults.standard
let accessToken = userDefaults.string(forKey: "oauth_token") ?? ""
let accessTokenSecret = userDefaults.string(forKey: "oauth_token_secret") ?? ""
let swifter = Swifter(consumerKey: TwitterConstants.CONSUMER_KEY, consumerSecret: TwitterConstants.CONSUMER_SECRET_KEY, oauthToken: accessToken, oauthTokenSecret: accessTokenSecret)
swifter.verifyAccountCredentials(includeEntities: false, skipStatus: false, includeEmail: true, success: { _ in
// Verify Succeed - Access Token is valid
completion(true)
}) { _ in
// Verify Failed - Access Token has expired
completion(false)
}
}
#IBAction func actionSubmit(_ sender: Any) {
self.actionTwitter()
}
func actionTwitter(){
//~~~~~~~~~~~~~~problem is here it is not taking provider as parameter
self.swifter.authorize(withProvider: provider as! ASWebAuthenticationPresentationContextProviding, callbackURL: URL(string: TwitterConstants.CALLBACK_URL)!) { (tokan: Credential.OAuthAccessToken?, resp: URLResponse) in
}
}
}
func failureHandler(){
}
extension twitterVc: SFSafariViewControllerDelegate{
func getUserProfile() {
self.swifter.verifyAccountCredentials(includeEntities: false, skipStatus: false, includeEmail: true, success: { json in
let userDefaults = UserDefaults.standard
userDefaults.set(self.accToken?.key, forKey: "oauth_token")
userDefaults.set(self.accToken?.secret, forKey: "oauth_token_secret")
// Twitter Id
if let twitterId = json["id_str"].string {
print("Twitter Id: \(twitterId)")
} else {
// self.twitterId = "Not exists"
}
// Twitter Handle
if let twitterHandle = json["screen_name"].string {
print("Twitter Handle: \(twitterHandle)")
} else {
// self.twitterHandle = "Not exists"
}
// Twitter Name
if let twitterName = json["name"].string {
print("Twitter Name: \(twitterName)")
} else {
// self.twitterName = "Not exists"
}
// Twitter Email
if let twitterEmail = json["email"].string {
print("Twitter Email: \(twitterEmail)")
} else {
// self.twitterEmail = "Not exists"
}
// Twitter Profile Pic URL
if let twitterProfilePic = json["profile_image_url_https"].string?.replacingOccurrences(of: "_normal", with: "", options: .literal, range: nil) {
print("Twitter Profile URL: \(twitterProfilePic)")
} else {
// self.twitterProfilePicURL = "Not exists"
}
print("Twitter Access Token: \(self.accToken?.key ?? "Not exists")")
}) { error in
print("ERROR: \(error.localizedDescription)")
}
}
}

im having difficulty getting downloaded json response into my data model and accessing it in code

i have the following code in which i'm trying to download exchange rates into my app to use in currency conversion.
The data fetch seems to work ok, as does the json decoding model, but i'm unable to get the data through the Rates variable
import SwiftUI
struct ExchangeRates: Codable {
var conversionRates: [String: Double]?
init(conversionRates:[String:Double]) {
self.conversionRates = conversionRates
}
enum CodingKeys: String, CodingKey {
case conversionRates = "conversion_rates"
}
}
class DownloadingData:ObservableObject{
#Published var Rates:ExchangeRates = ExchangeRates.init(conversionRates: ["test" : 0])
init() {
datatask()
}
func datatask() {
guard let url = URL(string: "https://v6.exchangerate-api.com/v6/********************/latest/GBP") else {return}
URLSession.shared.dataTask(with: url){(data,response,error) in
guard let data = data else {
print("no data")
return
}
guard error == nil else {
print("error :\(String(describing: error))")
return
}
guard let response = response as? HTTPURLResponse else {
print("invalid response")
return
}
guard response.statusCode >= 200 && response.statusCode < 300 else {
print("status code should be 2xx, but is \(response.statusCode)")
return
}
guard let rates = try? JSONDecoder().decode(ExchangeRates.self, from: data) else {return}
print(rates) **// this works and prints out data**
DispatchQueue.main.async {
[weak self] in
self?.Rates = rates
print(self?.Rates) **// this works and prints out the data**
}
print(self.Rates) **// this doesnt print out anything**
}.resume()
print(Rates) **// this doesnt print out anything**
}
}
i can't seem to get the data into the Rates Variable
any guidance please
thanks
here is a sample of the console output :
ExchangeRates(conversionRates: Optional(["test": 0.0]))
Optional(test3.ExchangeRates(conversionRates: Optional(["BZD": 2.7002, "PHP": 68.7948, "PGK": 4.725, "BND": 1.8176, "HNL": 32.8885, "TND": 3.7553, "BDT": 115.2218, "SBD": 10.6866, "NIO": 47.4824, "XDR": 0.963, "IDR": 19213.9064, "XCD": 3.6453, "CAD": 1.7152, "UGX": 4778.6135,])
you could try this, using your ExchangeRates struct:
class DownloadingData: ObservableObject{
#Published var rates = ExchangeRates(conversionRates: ["test" : 0])
init() {
datatask()
}
func datatask() {
guard let url = URL(string: "https://v6.exchangerate-api.com/v6/********************/latest/GBP") else {return}
URLSession.shared.dataTask(with: url){(data,response,error) in
guard let data = data else {
print("no data")
return
}
guard error == nil else {
print("error :\(error)")
return
}
guard let response = response as? HTTPURLResponse else {
print("invalid response")
return
}
guard response.statusCode >= 200 && response.statusCode < 300 else {
print("status code should be 2xx, but is \(response.statusCode)")
return
}
guard let exRates = try? JSONDecoder().decode(ExchangeRates.self, from: data) else {return}
print(exRates)
DispatchQueue.main.async {
self.rates = exRates // <--- here
print(self.rates) // <--- here
}
// print(self.rates) // <--- NEVER here
}.resume()
}
}
EDIT-1: with completion closure:
class DownloadingData: ObservableObject{
#Published var rates = ExchangeRates(conversionRates: ["test" : 0])
init() {
datatask() { isDone in
print(self.rates) // <--- here OK
}
}
func datatask(completion: #escaping(Bool) -> ()) { // <--- here
guard let url = URL(string: "https://v6.exchangerate-api.com/v6/********************/latest/GBP") else {return}
URLSession.shared.dataTask(with: url){(data,response,error) in
guard let data = data else {
print("no data")
return
}
guard error == nil else {
print("error :\(error)")
return
}
guard let response = response as? HTTPURLResponse else {
print("invalid response")
return
}
guard response.statusCode >= 200 && response.statusCode < 300 else {
print("status code should be 2xx, but is \(response.statusCode)")
return
}
guard let exRates = try? JSONDecoder().decode(ExchangeRates.self, from: data) else {return}
print(exRates) // <--- here OK
DispatchQueue.main.async {
self.rates = exRates
print(self.rates) // <--- here OK
completion(true) // <--- here return completion
}
}.resume()
}
}
Declare rates – please with starting lowercase letter –  as empty dictionary
#Published var rates = [String:Double]()
In the struct delete the init method and declare the dictionary also non-optional
struct ExchangeRates: Decodable {
let conversionRates: [String: Double]
enum CodingKeys: String, CodingKey {
case conversionRates = "conversion_rates"
}
}
in the DispatchQueue closure assign the value of conversionRates to rates
DispatchQueue.main.async { // no weak self needed
self.rates = rates.conversionRates
}
In the view enumerate the dictionary rates

How to Display Post Method Requests data in Swiftui Ios Simulator Screen?

This is Doc File Generate in Postman by write method and url "link/services.php".
This is Header File.
#Headers({"Content-Type: application/json"})
 #POST("services.php")
Observable<Division> divisionListApi(#Body HashMap<String, String> map);
map.put("method", "My_List");
After Postman Generation I Got This Given Below.
{"err_code":9,
"message":"My List ",
"list":[
{"ForestDivision":"Yam(T)"},
{"ForestDivision":"Rewar(T)"},
{"ForestDivision":"Ro(T)"},
{"ForestDivision":"Bh(T)"},
{"ForestDivision":"Ka(T) "},
{"ForestDivision":"Ambal(T) "},
{"ForestDivision":"Fari(T)"}]
}
I have made request by taking post mehod, is this the right way to create code in
swiftui.
import Foundation
import Combine
struct File: Decodable, Hashable {
var error_code : Int
var message : String
var list : [lisst]
}
struct lisst: Decodable, Hashable {
var ForestDivision: String
}
class fetchResults : ObservableObject{
#Published var fetchedRes : [lisst]?
func getData(completion: #escaping (File) -> ()){
print("Fetch")
let parameters = "{ I dont Know }"
let postData = parameters.data(using: .utf8)
var request = URLRequest(url: URL(string:
"URl")!,timeoutInterval: Double.infinity)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { (data, _, _) in
let resultList = try! JSONDecoder().decode(File.self, from: data!)
print(" ")
print("ID: \(resultList.error_code)")
print("VOLUME: \(resultList.message)")
print("READINGS: \(String(describing: resultList.list))")
print(" ")
print("SUCCESS: Got data - \(data! )")
DispatchQueue.main.async {
completion(resultList) // << here !!
}
}
task.resume()
}
}
Also How to Write in Swiftui View.
To Make Properly Works in List View.
import SwiftUI
import UIKit
struct DivisionList: View {
#ObservedObject var res = fetchResults()
var body: some View {
NavigationView {
Text("Nothing Here")
List(res.fetchedRes ?? [], id: \.self) { resp in // Error Here
ForEach(res.list, id: \.self) { course in
VStack {
Text(res.for) // Error Here
}
}
}
.navigationBarTitle("Data")
.onAppear(perform: {
self.getData // Error Here
})
}
}
}
struct DivisionList_Previews: PreviewProvider {
static var previews: some View {
DivisionList()
}
}
Create New Swift File Copy this Post Requests.
import Foundation
import Combine
let postUrl = "Your URl"
struct divisionList: Decodable, Identifiable {
let id: Int
let mesg: String
let list: [FDivision]
private enum CodingKeys: String, CodingKey {
case id = "err_code"
case mesg = "message"
case list = "list"
}
}
struct FDivision: Decodable, Hashable {
let forestDivision: String
private enum CodingKeys: String, CodingKey {
case forestDivision = "ForestDivision"
}
init(data:String) {
forestDivision = data
}
}
class viewModal: ObservableObject {
#Published var items = [FDivision]()
func postData() {
guard let serviceUrl = URL(string: postUrl) else { return }
let parameters: [String: Any] = [
"method": "Your Method Name"
var request = URLRequest(url: serviceUrl)
request.httpMethod = "POST"
request.setValue("Application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) else {
return
}
request.httpBody = httpBody
request.timeoutInterval = 20
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let dictionary = json as? [String:Any] {
if let arrList = dictionary["list"] as? [[String:Any]]{
for data in arrList{
let model = FDivision(data: data["ForestDivision"] as! String)
self.items.append(model)
}
}
}
print(json)
} catch {
print(error)
}
}
}.resume()
}
}
Create Now New Swiftui File.
import SwiftUI
import UIKit
struct DivisionList: View {
#ObservedObject var vm = viewModal()
var body: some View {
NavigationView {
VStack {
List(vm.items, id: \.self) { item in
Button(action: {}, label: {
Text(item.forestDivision).foregroundColor(.customGreen)
})
}
}.navigationBarTitle("Data")
.onAppear(perform: {
vm.postData()
})
}
}
}
struct DivisionList_Previews: PreviewProvider {
static var previews: some View {
DivisionList()
}
}