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()
}
}
Related
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.
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()
}
}
I have some difficulty using Combine in SwiftUI with making an API request and then decoding the data and returning it. When calling the API Service, it states in the 'AnyPublisher<UserLoginResponse, APIError>' that the result will be of such type. However, I would want to reuse the API Service and decode the response to different model structures. How can I call the API Service while defining which data structure it has to decode the returned data to? For example, in another ViewModel I would want to decode the API data to a 'NewsUpdatesResponse' instead of 'UserLoginResponse'. The code I have now is as follows:
Most code comes from: tundsdev
API Service
struct APIService {
func request(from endpoint: APIRequest, body: String) -> AnyPublisher<UserLoginResponse, APIError> {
var request = endpoint.urlRequest
request.httpMethod = endpoint.method
if endpoint.authenticated == true {
request.setValue("testToken", forHTTPHeaderField: "token")
}
if body != "" {
let finalBody = body.data(using: .utf8)
request.httpBody = finalBody
}
return URLSession
.shared
.dataTaskPublisher(for: request)
.receive(on: DispatchQueue.main)
.mapError { _ in APIError.unknown}
.flatMap { data, response -> AnyPublisher<UserLoginResponse, APIError> in
guard let response = response as? HTTPURLResponse else {
return Fail(error: APIError.unknown).eraseToAnyPublisher()
}
print(response.statusCode)
if response.statusCode == 200 {
let jsonDecoder = JSONDecoder()
return Just(data)
.decode(type: UserLoginResponse.self, decoder: jsonDecoder)
.mapError { _ in APIError.decodingError }
.eraseToAnyPublisher()
}
else {
return Fail(error: APIError.errorCode(response.statusCode)).eraseToAnyPublisher()
}
}
.eraseToAnyPublisher()
}
}
Login ViewModel
class LoginViewModel: ObservableObject {
#Published var loginState: ResultState = .loading
private var cancellables = Set<AnyCancellable>()
private let service: APIService
init(service: APIService) {
self.service = service
}
func login(username: String, password: String) {
self.loginState = .loading
let cancellable = service
.request(from: .login, body: "username=admin&password=admin")
.sink { res in
print(res)
switch res {
case .finished:
self.loginState = .success
case .failure(let error):
self.loginState = .failed(error: error)
}
} receiveValue: { response in
print(response)
}
self.cancellables.insert(cancellable)
}
}
the following is untested, but you could try something along this line, using generic Decodable:
struct APIService {
func request<T: Decodable>(from endpoint: APIRequest, body: String) -> AnyPublisher<T, APIError> {
var request = endpoint.urlRequest
request.httpMethod = endpoint.method
if endpoint.authenticated == true {
request.setValue("testToken", forHTTPHeaderField: "token")
}
if body != "" {
let finalBody = body.data(using: .utf8)
request.httpBody = finalBody
}
return URLSession
.shared
.dataTaskPublisher(for: request)
.receive(on: DispatchQueue.main)
.mapError { _ in APIError.unknown}
.flatMap { data, response -> AnyPublisher<T, APIError> in // <-- here
guard let response = response as? HTTPURLResponse else {
return Fail(error: APIError.unknown).eraseToAnyPublisher()
}
print(response.statusCode)
if response.statusCode == 200 {
let jsonDecoder = JSONDecoder()
return Just(data)
.decode(type: T.self, decoder: jsonDecoder) // <-- here
.mapError { _ in APIError.decodingError }
.eraseToAnyPublisher()
}
else {
return Fail(error: APIError.errorCode(response.statusCode)).eraseToAnyPublisher()
}
}
.eraseToAnyPublisher()
}
}
you may also want to return an array of such Decodable:
func requestThem<T: Decodable>(from endpoint: APIRequest, body: String) -> AnyPublisher<[T], APIError> {
....
.flatMap { data, response -> AnyPublisher<[T], APIError> in
...
.decode(type: [T].self, decoder: jsonDecoder)
...
The final solution which worked for me was the following with the help of workingdog.
API Service
struct APIService {
func request<T: Decodable>(ofType type: T.Type, from endpoint: APIRequest, body: String) -> AnyPublisher<T, Error> {
var request = endpoint.urlRequest
request.httpMethod = endpoint.method
if endpoint.authenticated == true {
request.setValue("testToken", forHTTPHeaderField: "token")
}
if body != "" {
let finalBody = body.data(using: .utf8)
request.httpBody = finalBody
}
return URLSession
.shared
.dataTaskPublisher(for: request)
.receive(on: DispatchQueue.main)
.mapError { _ in Error.unknown}
.flatMap { data, response -> AnyPublisher<T, Error> in
guard let response = response as? HTTPURLResponse else {
return Fail(error: Error.unknown).eraseToAnyPublisher()
}
print(response.statusCode)
let jsonDecoder = JSONDecoder()
if response.statusCode == 200 {
return Just(data)
.decode(type: T.self, decoder: jsonDecoder)
.mapError { _ in Error.decodingError }
.eraseToAnyPublisher()
}
else {
do {
let errorMessage = try jsonDecoder.decode(APIErrorMessage.self, from: data)
return Fail(error: Error.errorCode(statusCode: response.statusCode, errorMessage: errorMessage.error ?? "Er is iets foutgegaan")).eraseToAnyPublisher()
}
catch {
return Fail(error: Error.decodingError).eraseToAnyPublisher()
}
}
}
.eraseToAnyPublisher()
}
}
Login ViewModel
class LoginViewModel: ObservableObject {
#Published var loginState: ResultState = .loading
private var cancellables = Set<AnyCancellable>()
private let service: APIService
init(service: APIService) {
self.service = service
}
func login(username: String, password: String) {
self.loginState = .loading
let preparedBody = APIPrepper.prepBody(parametersDict: ["username": username, "password": password])
let cancellable = service.request(ofType: UserLoginResponse.self, from: .login, body: preparedBody).sink { res in
switch res {
case .finished:
self.loginState = .success
print(self.loginState)
case .failure(let error):
self.loginState = .failed(stateIdentifier: error.statusCode, errorMessage: error.errorMessage)
print(self.loginState)
}
} receiveValue: { response in
print(response)
}
self.cancellables.insert(cancellable)
}
}
Note that I have made some minor changes to the passing of the username and password parameters in the meantime.
I need to add author_id to the api so that it can give me all the articles of the author.
Here in SelectedAuthorView, I fetch related authorPost. In this authorPost it has author_id as well.
Below is my SelectedAuthorView, here I tried to fetch api data but first I do not want to fetch api inside the view. Second I do not know how to transfer this selected authorPost to my AuthorService
struct SelectedAuthorView: View {
var authorPost: AuthorPost
var body: some View {
NavigationView {
VStack {
AuthorListElementView(authorPost: authorPost)
if let selectedAuthorUrl = URL(string:"http://xxxx\(authorPost.author_id)/") {
let session = URLSession(configuration: .default)
var request = URLRequest(url: selectedAuthorUrl)
request.httpMethod = "GET"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: Any, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
}
}
}
}
}
}
Also this is the api I need to fetch data from [![enter image description here][1]][1]
Help is needed to be able to fetch data from that api.
You can use a ViewModel : ObservableObject
And your code will be like that :
The viewModel :
class ViewModel : ObservableObject {
func fetchData(authorPost: AuthorPost){
if let selectedAuthorUrl = URL(string:"http://yazar.io/api/author/article/list/\(authorPost.author_id)/") {
let session = URLSession(configuration: .default)
var request = URLRequest(url: selectedAuthorUrl)
request.httpMethod = "GET"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: Any, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
}
}
}
.
struct SelectedAuthorView: View {
var authorPost: AuthorPost
#ObservedObject var viewModel = ViewModel()
var body: some View {
NavigationView {
VStack {
AuthorListElementView(authorPost: authorPost)
}
}.onAppear{
self.viewModel.fetchData(authorPost:authorPost)
}
}
}
}
I'm trying to send an image from my application to telegram bot like here
https://newfivefour.com/swift-form-data-multipart-upload-URLRequest.html
Here is the code
let BotToken = "12345"
let ChatID = "123"
func SendToTelegram()
{
var request = URLRequest(url: URL(string: "https://api.telegram.org/bot"+BotToken+"/sendPhoto")!)
request.httpMethod = "POST"
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let params = [:] as [String: String]
UIGraphicsBeginImageContextWithOptions(ScreenImage.bounds.size, true, 0.0)
ScreenImage.image?.draw(in: CGRect(x: 0, y: 0, width: ScreenImage.frame.size.width, height: ScreenImage.frame.size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
request.httpBody = createBody(parameters: params,
boundary: boundary,
data: UIImageJPEGRepresentation(image!, 0.7)!,
mimeType: "image/jpg",
filename: "hello.jpg")
print(request.httpBody!)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
return
}
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? AnyObject
if let parseJSON = json {
print("resp :\(parseJSON)")
}
} catch let error as NSError {
print("error : \(error)")
}
}
task.resume()
}
and get an error
Bad Request: there is no photo in the request";
"error_code" = 400;
ok = 0;
Where do i make a mistake? I'm new in SWIFT and sorry for my English
I know this question is old but the link provided pushed me in the right direction. This is my solution for server side Swift not iOS but you should be able to use it with minimal changes. Remember if you're using iOS, perform none of these operations on the main thread.
class NetworkManager {
func sendTelegramPhoto(_ photo: Data) {
let url = "https://api.telegram.org/bot\(Constants.Telegram.token)/sendPhoto"
let params: [String: Any] = [
"chat_id": Constants.Telegram.uid,
"photo": photo
]
let _ = sendMultiTypePostRequest(url, parameters: params)
}
private func sendMultiTypePostRequest(_ url: String, parameters: [String:String]) -> NetworkResponse {
var networkResponse = NetworkResponse()
let semaphore = DispatchSemaphore(value: 0)
guard let url = URL(string: url) else {
return networkResponse
}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let httpBody = createBody(parameters: parameters,
boundary: boundary,
mimeType: "image/jpeg",
filename: "snapshot.jpg")
let config = URLSessionConfiguration.default
config.requestCachePolicy = .reloadIgnoringLocalCacheData
let session = URLSession(configuration: config)
let task = session.uploadTask(with: urlRequest, from: httpBody) { (data, response, error) in
networkResponse.data = data
networkResponse.response = response
networkResponse.error = error
semaphore.signal()
}
task.resume()
_ = semaphore.wait(timeout: .distantFuture)
return networkResponse
}
private func createBody(parameters: [String: String],
boundary: String,
mimeType: String,
filename: String) -> Data {
var body = Data()
let boundaryPrefix = "--\(boundary)\r\n"
for (key, value) in parameters {
if let data = value as? Data {
body.appendString(boundaryPrefix)
body.appendString("Content-Disposition: form-data; name=\"\(key)\"; filename=\"\(filename)\"\r\n")
body.appendString("Content-Type: \(mimeType)\r\n\r\n")
body.append(data)
body.appendString("\r\n")
} else if let string = value as? String {
body.appendString(boundaryPrefix)
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(string)\r\n")
}
}
body.appendString("--".appending(boundary.appending("--")))
return body
}
}
private extension Data {
mutating func appendString(_ string: String) {
let data = string.data(using: String.Encoding.utf8, allowLossyConversion: false)
append(data!)
}
}