I can't display String from API to View - ios

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()
}

Related

Receipt of notification of the arrival of new data from API

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()
}
}

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()
}
}
}

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")
}
}
}

Keep getting the error “Expected to decode Array<Any> but found a dictionary | Swift

I have the following JSON that is formatted like this:
{
"error":false
}
I understand that is not an array because it does not include square brackets on both sides, but I cannot seem to understand how to properly get Swift to interpret this correctly.
This is the structure I am using:
struct CheckStruct: Decodable {
let error: String?
}
And the following is the function that should read the JSON:
private func JSONFunc() {
guard let url = URL(string: "https://example.com/example/example.php"),
let value = name.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "number=\(number)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }
do {
self.CheckRecord = try JSONDecoder().decode(Array<CheckStruct>.self,from:data)
DispatchQueue.main.async {
// Do something
}
}
catch {
print(error)
}
}.resume()
}
UPDATE:
If I were to use the results of the function to create an if else statement, how would this look?
For example if results are true do this..
else do this...
Your model should be like this:
struct CheckStruct: Codable {
let error: Bool?
}
And your function should be like this:
private func JSONFunc() {
guard let url = URL(string: "https://example.com/example/example.php"),
let value = name.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "number=\(number)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else { return }
do {
let myData= try JSONDecoder().decode(CheckStruct.self, from:data)
print(myData.error)
} catch {
print(error)
}
}.resume()
}
BONUS
//Create Typealias
typealias BoolHandler = ((Bool?) -> Void)
//Create Function with Completion
private func fetchData(_ completion: #escaping BoolHandler) {
guard let url = URL(string: "https://example.com/example/example.php"),
let value = name.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "number=\(number)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else { return }
do {
let myData= try JSONDecoder().decode(CheckStruct.self, from:data)
DispatchQueue.main.async {
completion(myData.error)
}
} catch {
DispatchQueue.main.async {
completion(nil)
}
}
}.resume()
}
//Call Method
fetchData { isSuccess in
if isSuccess {
// Do something
} else {
// Do something
}
}
I hope it will work for you.
Enjoy.

Get full URL from short URL in Swift on iOS

Given a short URL https://itun.es/us/JB7h_, How do you expand it into the full URL? e.g. https://music.apple.com/us/album/blackstar/1059043043
Extension
extension URL {
func getExpandedURL() async throws -> Result<URL, Error> {
var request = URLRequest(url: self)
request.httpMethod = "HEAD"
let (_, response) = try await URLSession.shared.data(for: request)
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
throw URLError.unableToExpand
}
if let expandedURL = response.url {
return .success(expandedURL)
} else {
throw URLError.unableToExpand
}
}
enum URLError: Error {
case unableToExpand
}
}
Crude Demo
struct ContentView: View {
let shortURL = URL(string: "https://itun.es/us/JB7h_")
#State var expandedURLResult: Result<URL, Error>?
var body: some View {
Form {
Section("Short URL") {
Text(shortURL?.description ?? "")
}
Section("Long URL") {
switch expandedURLResult {
case .some(.success(let expandedURL)):
Text(expandedURL.description)
case .none:
Text("Waiting")
case .some(.failure(let error)):
Text(error.localizedDescription)
}
}
}
.task {
do {
expandedURLResult = try await shortURL?.getExpandedURL()
} catch {
expandedURLResult = .failure(error)
}
}
}
}
The final resolved URL will be returned to you in the NSURLResponse: response.URL.
You should also make sure to use the HTTP HEAD method to avoid downloading unnecessary data (since you don't care about the resource body).
Swift 4.2 Updated :
extension URL {
func resolveWithCompletionHandler(completion: #escaping (URL) -> Void) {
let originalURL = self
var req = URLRequest(url: originalURL)
req.httpMethod = "HEAD"
URLSession.shared.dataTask(with: req) { body, response, error in
completion(response?.url ?? originalURL)
}.resume()
}
Older Swift Versions:
extension NSURL
{
func resolveWithCompletionHandler(completion: NSURL -> Void)
{
let originalURL = self
let req = NSMutableURLRequest(URL: originalURL)
req.HTTPMethod = "HEAD"
NSURLSession.sharedSession().dataTaskWithRequest(req) { body, response, error in
completion(response?.URL ?? originalURL)
}.resume()
}
}
// Example:
NSURL(string: "https://itun.es/us/JB7h_")!.resolveWithCompletionHandler {
print("resolved to \($0)") // prints https://itunes.apple.com/us/album/blackstar/id1059043043
}

Resources