Receipt of notification of the arrival of new data from API - ios

I have data on from the api on my app
I am sending the new data from my site
I want to put a message or notification on the application about the arrival of new data for this API
What is the best way to do this task?
This is the data I got
struct VideoView_Msrhiat: View {
#StateObject var model = Api()
var body: some View {
VStack {
ScrollView(.vertical, showsIndicators: false) {
ForEach(model.models) { item in
VStack {
Text(item.title)
}
}
}
.onAppear() {
model.getData(url: APIgetURL.Tap1)
}
}
}
Also
struct model : Identifiable, Codable {
let id = UUID()
var color : String?
var details : String
}
class Api : ObservableObject{
#Published var models : [model] = []
func getData (url : String) {
guard let url = URL(string: url) else { return }
var request = URLRequest(url: url)
let token = "38|Xxxx"
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: request) { data, responce, err in
guard let data = data else { return }
do {
let dataModel = try JSONDecoder().decode([model].self, from: data)
DispatchQueue.main.async {
self.models = dataModel
} catch {
print("error: ", error)
}
}
resume()
}
}

Related

I can't display String from API to View

I tried to store the text in a variable of API
in the class
I do not want to transfer the entire model, I want to transfer the text as it is here
class Api : ObservableObject{
#Published var title : String = ""
#Published var details : String = ""
func getDataModelApi () {
guard let url = URL(string: APIgetURL.demo) else { return }
var request = URLRequest(url: url)
let token = "38|xxxxx"
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: request) { data, responce, err in
guard let data = data else { return }
do {
let dataModel = try JSONDecoder().decode([model].self, from: data)
for i in dataModel {
DispatchQueue.main.async {
self.title = i.title
self.details = i.details
}
}
} catch {
print("error: ", error)
}
}
.resume()
}
}
In the title variable, the value was stored successfully, but the display in the view does not get anything
struct ContentView: View {
#StateObject var model3 = Api()
var body: some View {
VStack {
Text(model3.title)
}
.onAppear() {
Api().getDataModelApi()
}
}
}
Here in getData, it shows the complete model and needs a link, which I want to access from getDataModelApi
#Published var models : [model] = []
func getData (url : String) {
guard let url = URL(string: url) else { return }
var request = URLRequest(url: url)
let token = "38|xxx"
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: request) { data, responce, err in
guard let data = data else { return }
do {
let dataModel = try JSONDecoder().decode([model].self, from: data)
DispatchQueue.main.async {
self.models = dataModel
}
} catch {
print("error: ", error)
}
}
.resume()
}
You are using two different instances of Api and you throw away the second one in onAppear.
Replace
.onAppear() {
Api().getDataModelApi()
}
With
.onAppear() {
model3.getDataModelApi()
}

Get a nested json in URL session?

{
"data":{
"email":"ms.lightwave#example.com",
"password":"123",
"token":""
}
}
struct JsonResult: View{
#State private var results = [GetData]()
var body: some View{
List(results, id: \.email){ item in
VStack(alignment: .leading) {
Text(item.password)
.font(.headline)
Text(item.token)
.font(.headline)
}
}.task {
await loadData()
}
}
struct Response : Codable {
var results: [GetData]
}
struct GetData: Codable{
var data : [Result]
}
struct Result: Codable {
var email: String
var password: String
var token: String
}
func loadData() async{
guard let url = URL(string: "MYURL") else {
print("invalid URL")
return
}
do{
let(data,_) = try await URLSession.shared.data(from: url)
// more code
if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data)
{
results = decodedResponse.results
}
} catch {
print("Invalid Data")
}
}
}
i need to know if the codable structure is right according to the structure of data i gave ? and also the fetching in list ? please i need help in the URLsession i am still new and i barely know about url alot !
i would be grateful if you help me ! thank you verrrry much !!!!
In the JSON there is no array involved (no [] at all).
The model corresponding to the JSON is
struct Response: Decodable {
let data : UserData
}
struct UserData: Decodable {
let email: String
let password: String
let token: String
}
So the data source cannot be declared as an array. To avoid an optional type create an enum with associated values indicating a state. The benefit is that you can show different views depending on the state
struct JsonResult: View {
enum LoadingState {
case idle, loading, loaded(UserData), failure(Error)
}
this is the rest of the struct, consider that there is no List either because UserData is a single object.
#State private var state : LoadingState = .idle
var body: some View {
VStack {
switch state {
case .idle: EmptyView()
case .loading: ProgressView()
case .loaded(let userData):
VStack(alignment: .leading) {
Text(userData.password)
.font(.headline)
Text(userData.email)
.font(.headline)
}
case .failure(let error): Text(error.localizedDescription)
}
}.task {
await loadData()
}
}
func loadData() async {
state = .loading
guard let url = URL(string: "MYURL") else {
state = .failure(URLError(.badURL))
return
}
do {
let (data,_) = try await URLSession.shared.data(from: url)
// more code
let decodedResponse = try JSONDecoder().decode(Response.self, from: data)
state = .loaded(decodedResponse.data)
} catch {
state = .failure(error)
print(error) // this shows the real DecodingError
}
}
}
struct Response : Decodable, Hashable {
var results: [GetData]
}
struct GetData: Decodable, Hashable{
var data : [Result]
}
struct Result: Decodable, Hashable {
var email: String
var password: String
var token: String
}
enum RequestError: Error {
case invalidURL
case missingData
}
class JsonResultViewModel: ObservableObject{
#Published var response = [Response]()
func performHTTPRequest(urlString: String) async throws{
guard let url = URL(string: urlString) else {throw RequestError.invalidURL}
guard let (data, resp) = try? await URLSession.shared.data(from: url) else{throw RequestError.invalidURL}
guard (resp as? HTTPURLResponse)?.statusCode == 200 else {throw RequestError.invalidURL}
let decoder = JSONDecoder()
guard let jsonResponse = try? decoder.decode([Response].self, from: data) else {throw RequestError.missingData}
DispatchQueue.main.async {
self.response = jsonResponse
}
}
}
struct ContentView: View {
#StateObject private var results = JsonResultViewModel()
var body: some View {
List(results.response.indices, id: \.self){ index in
VStack(alignment: .leading) {
Text(results.response[index].results[index].data[index].email)
.font(.headline)
Text(results.response[index].results[index].data[index].token)
.font(.headline)
}
}
.onAppear(perform: {
Task{
do {
try await results.performHTTPRequest(urlString: "wwww.url.com")
} catch RequestError.invalidURL{
print("invalid URL")
} catch RequestError.missingData{
print("missing data")
}
}
})
}
}

Pre-fill a webhook response into the text field in SwiftUI

I made a simple SwiftUI webhook app in Swift Playgrounds that surprisingly supports URLSession when making a HTTP request. Now what I would like to see if there is a way to take that response (for instance, from RequestBIN) after a POST request, and prefill it into a simple text area.
Here’s the code
struct ContentView: View {
#State private var TweetID = ""
var body: some View {
NavigationView {
Form {
Section(footer: Text("Where it should place the response data")) {
TextField("Field to place response data", text: $TweetID)
}
Section {
Button("Get Data") {
self.RequestTest()
}
}
}
}
}
func RequestTest() {
let semaphore = DispatchSemaphore (value: 0)
let parameters = "Foo=Bar"
let postData = parameters.data(using: .utf8)
var request = URLRequest(url: URL(string: "https://requestbin.net/r/OMG_IT_DOES")!,timeoutInterval: Double.infinity)
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
semaphore.signal()
return
}
print(String(data: data, encoding: .utf8)!)
semaphore.signal()
}
task.resume()
semaphore.wait()
}
}
note that I guess the code print(String(data: data, encoding: .utf8)!) that whereas data is the response needed to fill in the text field which is......I guess the ID or varible scope whatmacallsit $TweetID
try this example code to fetch the requested data and display it in your view:
EDIT-2: using completion closure for possible alert.
struct ContentView: View {
#State private var response = ""
#State private var showAlert = false
var body: some View {
NavigationView {
Form {
Section(footer: Text(response)) {
TextField("Field to place response data", text: $response)
}
Section {
Button("Get Data") {
requestTest() { results in
response = results
if response == "No Data!" {
showAlert = true
}
}
}
}
}
.alert(isPresented: $showAlert) {
Alert(title: Text("alertTitle"), message: Text("error"), dismissButton: .default(Text("OK")))
}
}
}
func requestTest(completion: #escaping(String) -> ()) { // <--- here completion closure
if let url = URL(string: "https://requestbin.net/r/d21yhb0r") {
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
var components = URLComponents(url: url, resolvingAgainstBaseURL: false)!
components.queryItems = [ URLQueryItem(name: "Foo", value: "BAR") ]
if let query = components.url!.query {
request.httpBody = Data(query.utf8)
}
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data,
let apiResponse = String(data: data, encoding: .utf8) {
completion(apiResponse) // <--- here
} else {
completion("No Data!") // <--- here
}
}
task.resume()
}
}
}

How can i use this new get method for other models?

class Network {
func getingData(completion : #escaping ([Model]) -> ()) async {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
do {
let (data, _) = try await URLSession.shared.data(from: url)
if let posts = try? JSONDecoder().decode([Model].self, from: data) {
completion(posts)
}
}
catch {
print("error")
}
}
}
You could try something like this approach, where getData works with Decodable, as was mentioned in the previous answer.
In this particular example an array of Decodable.
struct Post: Decodable, Identifiable {
let userId: Int
let id: Int
let title: String
let body: String
var comments: [Comment]?
}
struct Comment: Decodable, Identifiable {
let postId: Int
let id: Int
let name: String
let email: String
let body: String
}
struct ContentView: View {
let client = Network()
#State var posts: [Post] = []
var body: some View {
List {
ForEach(posts, id: \.id) { post in
Text(post.title)
}
}
.task {
posts = await client.getData(from: "https://jsonplaceholder.typicode.com/posts")
// all comments from the first post
let comments: [Comment] = await client.getData(from: "https://jsonplaceholder.typicode.com/posts/\(posts[0].id)/comments")
print("\n---> comments: \(comments)")
}
}
}
class Network {
func getData<T: Decodable>(from urlString: String) async -> [T] {
guard let url = URL(string: urlString) else {
print(URLError(.badURL))
return [] // <-- todo, deal with errors
}
do {
let (data, response) = try await URLSession.shared.data(for: URLRequest(url: url))
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
print(URLError(.badServerResponse))
return [] // <-- todo, deal with errors
}
let results = try JSONDecoder().decode([T].self, from: data)
return results
}
catch {
return [] // <-- todo, deal with errors
}
}
}
Is it what you're looking for?
import Foundation
class Network {
func getingData<Model: Decodable>(completion : #escaping ([Model]) -> ()) async {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else {
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
if let posts = try? JSONDecoder().decode([Model].self, from: data) {
completion(posts)
}
} catch {
print("error")
}
}
}
If so, you only need to declare the Model type as generic. The only thing you need Model to conform is Decodable (the requirement of the JSONDecoder().decode([Model].self, from: data) call).

Updating List from JSON data SwiftUI

I'm having a hard time understanding how JSON data is supposed to be updated in a List in SwiftUI. I'm fetching data from NewsAPI.org, my list and detail views work just fine. I'm trying to figure out how to keep the list up-to-date when my json data changes, but the data remains outdated. I'm still a beginner to swift so if I made a mistake any help would be greatly appreciated.
UPDATED
Attempted to use combine with the same results, outdated data
New data class
class NewsData: ObservableObject {
var objectWillChange = PassthroughSubject<NewsData, Never>()
#Published var articles = [Article]() {
willSet {
objectWillChange.send(self)
}
}
init() {
guard let url = URL(string: "http://newsapi.org/v2/top-headlines?country=us&apiKey=API_KEY") else { return }
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
if let response = try? JSONDecoder().decode(News.self, from: data) {
DispatchQueue.main.async() {
self.articles = response.articles
}
}
}
}
.resume()
}
/*
init() {
URLSession.shared
.dataTaskPublisher(for: URLRequest(url: URL(string: "http://newsapi.org/v2/top-headlines?country=us&apiKey=API_KEY")!))
.map(\.data)
.decode(type: News.self, decoder: JSONDecoder())
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
break
case .failure(let error):
print(error.localizedDescription)
}
}, receiveValue: { data in
self.articles = data.articles
})
.store(in: &self.cancellables)
}
*/
/*
init() {
load()
}
func load() {
guard let url = URL(string: "http://newsapi.org/v2/top-headlines?country=us&apiKey=API_KEY") else { return }
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
if let response = try? JSONDecoder().decode(News.self, from: data) {
DispatchQueue.main.async() {
self.articles = response.articles
}
}
}
}
.resume()
}
*/
}
My data old class
struct News : Codable {
var articles : [Article]
}
struct Article : Codable {
let description : String?
let title : String?
let author: String?
let source: Source
let content: String?
let publishedAt: String?
}
struct Source: Codable {
let name: String?
}
class NewsData: ObservableObject {
#Published var news: News = News(articles: [])
init() {
load()
}
func load() {
guard let url = URL(string: "http://newsapi.org/v2/top-headlines?country=us&apiKey=API_KEY_HERE") else { return }
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
if let response = try? JSONDecoder().decode(News.self, from: data) {
DispatchQueue.main.async() {
self.news = response
}
}
}
}
.resume()
}
}
My ContentView
func relativeDate(date: String) -> String {
let formatter = RelativeDateTimeFormatter()
let dateFormatter = ISO8601DateFormatter()
return formatter.localizedString(for: dateFormatter.date(from: date) ?? Date(), relativeTo: Date())
}
struct ContentView: View {
#ObservedObject var news: NewsData
var body: some View {
NavigationView {
List(news.news.articles , id: \.title) { article in
VStack (alignment: .leading, spacing: 5){
Text(article.title ?? "")
.fontWeight(.bold)
.font(.subheadline)
.lineLimit(1)
Text(article.description ?? "")
.font(.subheadline)
.foregroundColor(.secondary)
.lineLimit(1)
Text(relativeDate(date: article.publishedAt ?? ""))
.font(.subheadline)
.foregroundColor(.secondary)
}
}
.navigationTitle("News")
}
}
}

Resources