I am trying insert into db use post method in swift
and i can not get the response.statusCode of post method in swiftui view
how can i fix it.
the variable self.resp should be 201 in view when I insert successfully, but
it was 0.
import SwiftUI
struct ContentView: View {
#State var resp:Int = 0
var body: some View {
VStack{
Text("Hello, World!")
Button(action: {
self.resp = postAdd()
print("view\(self.resp)")
}) {
Text(/*#START_MENU_TOKEN#*/"Button"/*#END_MENU_TOKEN#*/)
.background(self.resp == 201 ? Color.green : Color.gray)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
func postAdd() ->Int{
let url = URL(string: "http://localhost:3000/user")
var request = URLRequest(url: url!)
var resp = 0
request.httpMethod = "POST"
let urlstr = "id=5&name=aaa"
request.httpBody = urlstr.data(using: .utf8)!
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if error == nil, let data = data, let response = response as? HTTPURLResponse {
print("Content-Type: \(response.allHeaderFields["Content-Type"] ?? "")")
print("statusCode: \(response.statusCode)")
resp = response.statusCode
print(String(data: data, encoding: .utf8) ?? "")
}
}.resume()
return resp
}
try this:
as i wrote in the comment - you have to know which calls are async and which are sync. I hope you know what sync and async calls are - if not, please google for it. if yes: the data task call is async and so it returns directly the resp = 0 value. After some time when the urlrequest is ready, it continuous in this line : "{ (data, response, error) in", but here it cannot return a value anymore because the method already returned a value. So with the completion pattern you can be sure you will get called when the async call is ready.
struct ContentView: View {
#State var resp:Int = 0
var body: some View {
VStack{
Text("Hello, World!")
Button(action: {
postAdd() { response in
self.resp = response
print("view\(self.resp)")
}
}) {
Text(/*#START_MENU_TOKEN#*/"Button"/*#END_MENU_TOKEN#*/)
.background(self.resp == 201 ? Color.green : Color.gray)
}
}
}
}
func postAdd(completion:#escaping(Int)->()) {
let url = URL(string: "http://localhost:3000/user")
var request = URLRequest(url: url!)
request.httpMethod = "POST"
let urlstr = "id=5&name=aaa"
request.httpBody = urlstr.data(using: .utf8)!
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if error == nil, let data = data, let response = response as? HTTPURLResponse {
print("Content-Type: \(response.allHeaderFields["Content-Type"] ?? "")")
print("statusCode: \(response.statusCode)")
completion(response.statusCode)
print(String(data: data, encoding: .utf8) ?? "")
} else {
completion(404)
}
}.resume()
}
Related
I've been trying to get a list of entities from a graphql endpoint but I can't figure it out. Also, the console in my Xcode v13.4 isn't showing anything even though I have some print() statements in the code, so that's not helping - I've found where it is at the bottom of the window but it's always blank.
My View to get the data is below, the DetailView is the link following a link from the main ContentView. the loadData function content, I got the code from Postman after testing the graphql call.
import SwiftUI
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
//array of properties
struct Response: Codable {
var data: Properties
}
struct Properties: Codable {
var properties: Nodes
}
struct Nodes: Codable {
var nodes: [Result]
}
struct Result: Codable {
var propertyId: Int
var title: String
}
struct DetailView: View {
#State private var results = [Result]()
var body: some View {
List(results, id: \.propertyId) { property in
VStack(alignment: .leading) {
Text(property.title)
.font(.headline)
}
}
.task{
await loadData()
}
}
func loadData() async {
let semaphore = DispatchSemaphore (value: 0)
let parameters = "{\"query\":\"{\\n properties {\\n nodes {\\n title(format: RENDERED)\\n propertyId\\n }\\n }\\n}\",\"variables\":{}}"
let postData = parameters.data(using: .utf8)
var request = URLRequest(url: URL(string: "http://DOMAIN/graphql")!,timeoutInterval: Double.infinity)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = postData
do {
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
semaphore.signal()
return
}
if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
results = decodedResponse.data.properties.nodes
}
semaphore.signal()
}
task.resume()
semaphore.wait()
} catch {
print("Invalid data")
}
}
}
The output of the graphql call is
{
"data": {
"properties": {
"nodes": [
{
"title": "MY TITLE",
"propertyId": 00001
}
]
}
}
}
EDIT
Swapped try? for try! as suggested
if let decodedResponse = try! JSONDecoder().decode(Response.self, from: data) {
results = decodedResponse.data.properties.nodes
}
error: Initializer for conditional binding must have Optional type, not 'Response'
I managed to get it all working with the below...
import SwiftUI
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
//array of properties
struct Response: Codable {
var data: Properties
}
struct Properties: Codable {
var properties: Nodes
}
struct Nodes: Codable {
var nodes: [Result]
}
struct Result: Codable {
var propertyId: Int
var title: String
}
struct DetailView: View {
#State private var results = [Result]()
#State private var test = "one"
var body: some View {
List(results, id: \.propertyId) { property in
VStack(alignment: .leading) {
Text(property.title)
.font(.headline)
}
}.task{
await loadData()
}
}
func loadData() async {
let semaphore = DispatchSemaphore (value: 0)
let parameters = "{\"query\":\"{\\n properties {\\n nodes {\\n title(format: RENDERED)\\n propertyId\\n }\\n }\\n}\",\"variables\":{}}"
let postData = parameters.data(using: .utf8)
var request = URLRequest(url: URL(string: "https://DOMAIN/graphql")!,timeoutInterval: Double.infinity)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = postData
do {
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
semaphore.signal()
return
}
let decodedResponse = try! JSONDecoder().decode(Response.self, from: data)
results = decodedResponse.data.properties.nodes
semaphore.signal()
}
task.resume()
semaphore.wait()
} catch {
print("Invalid data \(error)")
}
}
}
Much to learn :)
This question already has answers here:
Returning data from async call in Swift function
(13 answers)
Closed 1 year ago.
First of all, this is my first attempt at Swift so I'm not really sure what I'm doing. I'm learning as I go right now and have hit a roadblock.
I'm trying to implement a WatchOS app that will call an API on a set timer to track fluctuations in some crypto prices.
I have figured out how to make the API call and get the JSON parsed to a point where I can print the data but I'm struggling to get it out of the closure and to my interface. I know the proper way to do this is with a completion handler but I can't seem to get a solid understanding of how to make that work in this scenario.
Any help would be appreciated
import SwiftUI
var refresh = bitcoin()
var btc: String = refresh
var eth: String = "ETH"
var doge: String = "DOGE"
struct ContentView: View {
var body: some View {
VStack(alignment: .leading ){
Label("\(btc)", image: "eth").padding(.vertical, 10.0)
Label("\(eth)", image: "eth").padding(.vertical, 10.0)
Label("\(doge)", image: "doge").padding(.vertical, 10.0)
}
.scaledToFill()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
struct responseData: Codable{
let data: Response?
}
struct Response: Codable{
var id: String
var rank: String
var symbol: String
var name: String
var supply: String
var maxSupply: String
var marketCapUsd: String
var volumeUsd24Hr: String
var priceUsd: String
var changePercent24Hr: String
var vwap24Hr: String
}
func bitcoin() -> String{
var result: String = "btc"
var request = URLRequest(url: URL(string: "https://api.coincap.io/v2/assets/bitcoin")!,timeoutInterval: Double.infinity)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
return
}
let response = try! JSONDecoder().decode(responseData.self, from: data)
result = (response.data?.priceUsd)!
print(result)
}
task.resume()
return result
}
There many ways to achieve what you want, one way is to use "ObservableObject". Try something like this:
import SwiftUI
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class CoinModel: ObservableObject {
#Published var btcPriceUsd = "not yet available"
#Published var ethPriceUsd = "not yet available"
#Published var dogePriceUsd = "not yet available"
}
struct ContentView: View {
#StateObject var coins = CoinModel()
var body: some View {
VStack(alignment: .leading ){
Label("\(coins.btcPriceUsd)", image: "btc").padding(.vertical, 10.0)
Label("\(coins.ethPriceUsd)", image: "eth").padding(.vertical, 10.0)
Label("\(coins.dogePriceUsd)", image: "doge").padding(.vertical, 10.0)
}
.scaledToFill()
.onAppear {
// bitcoin()
bitcoin2 { price in
coins.btcPriceUsd = price
}
}
}
func bitcoin() {
var request = URLRequest(url: URL(string: "https://api.coincap.io/v2/assets/bitcoin")!,timeoutInterval: Double.infinity)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
return
}
let response = try! JSONDecoder().decode(responseData.self, from: data)
if let respData = response.data {
DispatchQueue.main.async {
coins.btcPriceUsd = respData.priceUsd
}
}
}
task.resume()
}
}
EDIT: if you really want to use completion, then try this:
func bitcoin2(completion: #escaping (String) -> Void) {
var request = URLRequest(url: URL(string: "https://api.coincap.io/v2/assets/bitcoin")!,timeoutInterval: Double.infinity)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
return completion("")
}
let response = try! JSONDecoder().decode(responseData.self, from: data)
if let respData = response.data {
DispatchQueue.main.async {
completion(respData.priceUsd)
}
}
}
task.resume()
}
I was making a search bar with onchange in which it recieve some data for every change. But sometime when I type fast the working is not proper. I think I need to use debounce here. I've tried and fails (beginner to swift)
Here is my code :
struct Result: Codable{
var searchResult :[Real]
}
struct Real: Codable{
var _id : String
var name : String
}
struct ContentView: View { #State private var text: String = ""
#State private var isEditing = false
#State private var results = [Result]()
#State private var real = [Real]()
var body: some View {
VStack(alignment: .leading){
HStack {
TextField("Search ...", text: $text)
.onChange(of: text) {
guard let url = URL(string: "https://'api link'")else{
print("inavlid url")
return
}
let json: [String: Any] = ["searchKey":text]
let jsonData = try? JSONSerialization.data(withJSONObject: json)
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = jsonData
URLSession.shared.dataTask(with: request){
data,response,error in
if let data = data{
do {
let decodedResponse = try JSONDecoder().decode(Result.self, from: data)
DispatchQueue.main.async {
self.real = decodedResponse.searchResult
}
print("tommtoow")
} catch let jsonError as NSError {
print("JSON decode failed: \(jsonError)")
}
return
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
print($0)
}
}
}
}
}
}
}
Try to implement a general API request with a method .onAppear(perform: ) and onChange can be used after to filter results
with "onChange" you do your request every time you type something, hence your issue. What you probably want is to use "onSubmit" (ios15) or "onCommit" (will be deprecated), that way you do your request only when you are finished typing it in.
Hey so I'm relatively new to Swift and I have successfully fetched data from a public API but there's another one that requires authentication. I do have an account and I've verified that the credentials work, however it's not clear to me how to write the POST request with Basic Auth
I see on the Wiki page for Basic Auth it says to configure the URL with the user & pass in it like this: https://Aladdin:OpenSesame#www.example.com/index.html, but this alone doesn't fix it so I'm not sure what else I need to do to configure this request. Any help would be appreciated!
struct Response: Codable {
var results: [Result]
}
struct Result: Codable {
var pool_system_type: String
}
struct WaterTestView: View {
#State private var results = [Result]()
var body: some View {
List(results, id: \.trackId) { result in
VStack(alignment: .leading) {
Text(result.pool_system_type)
}
}.onAppear(perform: loadData)
}
func loadData() {
guard let url = URL(string: "https://myemail#email.com:mypassword#biolabhydra.com/api/v3/water_tests") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
DispatchQueue.main.async {
results = decodedResponse.results
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}
For basic auth you put the authentication data in an HTTP header field.
extension URLRequest {
mutating func setBasicAuth(username: String, password: String) {
let encodedAuthInfo = String(format: "%#:%#", username, password)
.data(using: String.Encoding.utf8)!
.base64EncodedString()
addValue("Basic \(encodedAuthInfo)", forHTTPHeaderField: "Authorization")
}
}
var request = URLRequest(url: URL(string: "https://www.awesome.com/")!)
request.setBasicAuth(username: "awesomeUser", password: "awesomePass")
request.httpMethod = "POST"
When I run the following code I just get one error: abort trap: 6. Anyone have any idea? I already tried deleting the derived data file.
class ImageViewModel: ObservableObject {
#Published var image: UIImage = nil
let session = URLSession.shared
func loadImage() {
guard let url = URL(string: "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
session.dataTask(with: request) {data, response, error in
guard let data = data else {return}
DispatchQueue.main.async() {
self.image = UIImage(data: data)
}
}
}
}
struct ImageView: View {
#ObservedObject var viewModel = ImageViewModel()
var body: some View {
Image(uiImage: viewModel.image)
.onAppear(perform: loadImage)
// Text("hi")
}
func loadImage() {
viewModel.loadImage()
}
}
No worries, it was because of this:
var image: UIImage = nil
And because I didn't add:
.resume()