everyone. I have a problem. I can't figure out. I want get data ( JSON array ) from my server, it doesn't work correctly. It doesn't show nothing. HTTP header: text/plain . I use this source as example: https://medium.com/#rbreve/displaying-a-list-with-swiftui-from-a-remote-json-file-6b4e4280a076 . It's my code and data:
import Foundation
import SwiftUI
import Combine
struct Medewerker: Codable, Identifiable, Hashable {
public var id : Int
public var naam: String
public var voornaam: String
}
struct SwiftUIView2: View {
#ObservedObject var fetcher = MedewerkerFetcher()
var body: some View {
VStack {
List(fetcher.medewerker) { medewerker in
VStack (alignment: .leading) {
Text(medewerker.naam)
Text(medewerker.voornaam)
.font(.system(size: 11))
.foregroundColor(Color.gray)
}
}
}
}
}
struct SwiftUIView2_Previews: PreviewProvider {
static var previews: some View {
SwiftUIView2()
}
}
public class MedewerkerFetcher: ObservableObject {
#Published var medewerker = [Medewerker]()
init(){
load()
}
func load() {
let url = URL(string: "http://anwin.be/src/public/medewerker")!
var request = URLRequest(url: url)
request.addValue("text/plain", forHTTPHeaderField: "Accept")
URLSession.shared.dataTask(with: request) {(data,response,error) in
if let mimeType = response?.mimeType, mimeType == "text/plain"{
do {
if let d = data {
let decodedLists = try JSONDecoder().decode([Medewerker].self, from: d)
DispatchQueue.main.async {
self.medewerker = decodedLists
}
}else {
print("No Data")
}
} catch {
print ("Error")
}
}
}.resume()
}
}
Data. Json from server:
[
{
"id": 1,
"naam": "Fanoberov",
"voornaam": "Andre"
},
{
"id": 2,
"naam": "Kunitski",
"voornaam": "Dzmitry"
},
{
"id": 7,
"naam": "Karim",
"voornaam": "Nassar"
}
]
Can you help me, please ?
Thank you.
error is:
{Error Domain=kCFErrorDomainCFNetwork Code=-1022 "(null)"}, NSErrorFailingURLKey=http://dtcws.azurewebsites.net/ShowImg.aspx?params=dtc_376_0_True_False_22, NSLocalizedDescription=The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.})
you have to change your info.plist
add this:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Related
So, new to swift and was able to peice this together by reading and watching videos however i reached a point after countless hours searching for a solution..
Basically what the app does is scan's a qr code, parses the url it reads from the qr code to get a key, then I am appending that key to the api url, and i want to output the results from the api to the screen. however I am receiving an error Type '()' cannot conform to 'View' in xcode
Here is sample json data
[
{
"id": "160468",
"sport": "BASKETBALL",
"year": "2020",
"brand": "PANINI PRIZM",
"cardNumber": "278",
"playerName": "LaMELO BALL",
"extra": "",
"gradeName": "MINT",
"grade": "9",
"serial": "63585906",
"authDate": "1656406800",
"link": "https://www.example.com/certificate-verification?certificateNumber=63585906"
}
]
here is my contentview
import SwiftUI
import CodeScanner
extension URL {
var components: URLComponents? {
return URLComponents(url: self, resolvingAgainstBaseURL: false)
}
}
extension Array where Iterator.Element == URLQueryItem {
subscript(_ key: String) -> String? {
return first(where: { $0.name == key })?.value
}
}
struct Card: Decodable {
let sport: String
let year: String
let brand: String
let cardNumber: String
let playerName: String
let extra: String
let gradeName: String
let grade: String
let serial: String
}
struct ContentView: View {
#State var isPresentingScanner = false
#State var scannedCode: String = ""
var scannerSheet : some View {
CodeScannerView(
codeTypes: [.qr],
completion: { result in
if case let .success(code) = result {
self.scannedCode = code.string
self.isPresentingScanner = false
}
}
)
}
func getQueryStringParameter(url: String, param: String) -> String? {
guard let url = URLComponents(string: url) else { return nil }
return url.queryItems?.first(where: { $0.name == param })?.value
}
var body: some View {
VStack(spacing: 10) {
Image("logo-white")
.offset(y: -200)
if let urlComponents = URL(string: scannedCode)?.components,
let cert = urlComponents.queryItems?["certificateNumber"] {
//Text(cert)
let apihit = URL(string: "https://app.example.com/api.php?apikey=xxx&cert=\(cert)")!
//Text(apihit.absoluteString)
var request = URLRequest(url: apihit)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.dataTask(with: apihit) { data, response, error in
if let data = data {
if let cards = try? JSONDecoder().decode([Card].self, from: data) {
print(cards)
} else {
print("Invalid Response")
}
} else if let error = error {
print("HTTP Request Failed \(error)")
}
}
}
Button("Scan QR Code") {
self.isPresentingScanner = true
}
.padding()
.background(Color(red: 0, green: 0, blue: 0.5))
.foregroundColor(.white)
.clipShape(Rectangle())
.cornerRadius(20)
.sheet(isPresented: $isPresentingScanner) {
self.scannerSheet
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.gray)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I have tried countless tutorials online, however none of them show how to do it within the view, which I believe is where it belongs because I don't get the actual json url until after I read the qr code..
To populate a link within a view, use Webkit.
First you need to import Webkit. Then, create a WebView which conforms to the UIViewRepresentable protocol.
Here is the Apple documentation. This is the easiest way to integrate web content into your app.
I hope this helps!
NOTE: Currently config: Xcode 13 beta 3 (13A5192i), macOS Montery (21A5284e).
Trying to asynchronously initialize data from a json.
The code apparently works fine, but the elements are not added to #Published public private (set) var users = [User]()
Is it something stupid I don't see or is it just another bug?
[
{
"id": 1,
"name":"gio"
},
{
"id": 3,
"name":"mimi"
},
{
"id": 2,
"name":"pepo"
},
{
"id": 4,
"name":"bassix"
},
{
"id": 5,
"name":"peponews"
}
]
import SwiftUI
struct URLWatcher: AsyncSequence, AsyncIteratorProtocol {
typealias Element = Data
let url: URL
let delay: Int
private var comparisonData: Data?
init(url: URL, delay: Int = 10) {
self.url = url
self.delay = delay
}
mutating func next() async throws -> Data? {
if comparisonData == nil {
comparisonData = try await fetchData()
} else {
while true {
await Task.sleep(UInt64(delay) * 1_000_000_000)
let latestData = try await fetchData()
if latestData != comparisonData {
comparisonData = latestData
break
}
}
}
if comparisonData == nil {
return nil
} else {
return comparisonData
}
}
private func fetchData() async throws -> Data {
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
func makeAsyncIterator() -> URLWatcher {
self
}
}
struct User: Identifiable, Decodable {
let id: Int
let name: String
}
#MainActor
class UserData: ObservableObject {
static let shared = UserData()
#Published public private(set) var users = [User]()
func fetchUsers() async {
let url = Bundle.main.url(forResource: "items", withExtension: "json")
let urlWatcher = URLWatcher(url: url!, delay: 3)
do {
for try await data in urlWatcher {
try withAnimation {
let data = try JSONDecoder().decode([User].self, from: data)
print(data)
users = data
}
}
} catch {
print("error \(error)")
}
}
private init() {
// Begin loading the data
Task {
await fetchUsers()
}
}
}
struct TestAsync: View {
let data = UserData.shared
var body: some View {
List(data.users) { user in
Text(user.name)
}
}
}
try this:
struct TestAsync: View {
#StateObject var data = UserData.shared // <-- or #ObservedObject
var body: some View {
List(data.users) { user in
Text(user.name)
}
}
}
I'm stuck on this.
I have a json that I'm parsing and that Json has an entry value and I want to do a swift list based in user input that will trigger from the external json different responses.
I have the following code so far
import SwiftUI
import Combine
import Foundation
var lokas : String = "ABCY"
struct ContentView: View {
#State var name: String = ""
#ObservedObject var fetcher = Fetcher()
var body: some View {
VStack {
TextField("Enter Loka id", text: $name)
List(fetcher.allLokas) { vb in
VStack (alignment: .leading) {
Text(movie.device)
Text(String(vb.seqNumber))
.font(.system(size: 11))
.foregroundColor(Color.gray)
}
}
}
}
}
public class Fetcher: ObservableObject {
#Published var allLokas = [AllLoka_Data]()
init(){
load(devices:lokas)
}
func load(devices:String) {
let url = URL(string: "https://xxx.php?device=\(devices)&hours=6")!
URLSession.shared.dataTask(with: url) {(data,response,error) in
do {
if let d = data {
let decodedLists = try JSONDecoder().decode([AllLoka_Data].self, from: d)
DispatchQueue.main.async {
self.allLokas = decodedLists
}
}else {
print("No Data")
}
} catch {
print ("Error")
}
}.resume()
}
}
struct AllLoka_Data: Codable {
var date: String
var time: String
var unix_time: Int
var seqNumber : Int
}
// Now conform to Identifiable
extension AllLoka_Data: Identifiable {
var id: Int { return unix_time }
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
But I just added a Global variable to test it, but how do I pass the variable name to make the function work ?
Thank you
I've tried to parse JSON from https://jsonplaceholder.typicode.com/photos
Everything works great, but instead of getting a picture I get only text with the URL of this particular picture.
Here is my code. What did I do wrong? Thank you in advance!
.
.
.
.
.
.
import SwiftUI
struct ContentView: View {
#State var posts = [Post]()
var body: some View {
NavigationView{List(posts, id: \.albumId) { post in
NavigationLink(destination: DetailView(post: post)) {
HStack() {
Text(String(describing: post.albumId))
.font(.headline)
}
}.navigationBarTitle("Albums")
}.onAppear(perform: loadData)
}
}
}
struct DetailView: View {
var post: Post
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text(post.title)
.font(.headline)
Text(post.url)
}
}
}
struct Post: Decodable {
var albumId: Int
var title: String
var url: String
}
extension ContentView
{
func loadData() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/photos") else {
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
if let response_obj = try? JSONDecoder().decode([Post].self, from: data) {
DispatchQueue.main.async {
self.posts = response_obj
}
}
}
}.resume()
}
}
Use https://github.com/SDWebImage/SDWebImageSwiftUI
var body: some View {
WebImage(url: URL(string: "http://...."))
}
I'm having some trouble fetching data from the PiHole API;
This is the JSON format (from the url http://pi.hole/admin/api.php?summary):
{
"domains_being_blocked": "1,089,374",
"dns_queries_today": "34,769",
"ads_blocked_today": "11,258",
"ads_percentage_today": "32.4",
"unique_domains": "9,407",
"queries_forwarded": "17,972",
"queries_cached": "5,539",
"clients_ever_seen": "35",
"unique_clients": "23",
"dns_queries_all_types": "34,769",
"reply_NODATA": "1,252",
"reply_NXDOMAIN": "625",
"reply_CNAME": "10,907",
"reply_IP": "21,004",
"privacy_level": "0",
"status": "enabled",
"gravity_last_updated": {
"file_exists": true,
"absolute": 1588474361,
"relative": {
"days": "0",
"hours": "14",
"minutes": "18"
}
}
}
This is my code:
ContentView.swift
import SwiftUI
struct NetworkController {
static func fetchData(completion: #escaping (([PiHole.Stat]) -> Void)) {
if let url = URL(string: "http://pi.hole/admin/api.php?summary") {
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let data = data {
let stat = try? JSONDecoder().decode(PiHole.self, from: data)
completion(stat?.stats ?? [])
}
}.resume()
}
}
}
class ContentViewModel: ObservableObject {
#Published var messages: [PiHole.Stat] = []
func fetchData() {
NetworkController.fetchData { messages in
DispatchQueue.main.async {
self.messages = messages
}
}
}
}
struct ContentView: View {
#ObservedObject var viewModel = ContentViewModel()
var body: some View {
List {
ForEach(viewModel.messages, id: \.self) { stat in
Text(stat.domains_being_blocked)
}
}.onAppear{
self.viewModel.fetchData()
}
}
}
Data.swift
struct PiHole: Decodable {
var stats: [Stat]
struct Stat: Decodable, Hashable {
var domains_being_blocked: String
var ads_percentage_today: String
var ads_blocked_today: String
var dns_queries_today: String
}
}
Everything seems okay, no errors, yet when I run it, the simulator only shows an empty list
In Playground I can retrieve those data just fine:
import SwiftUI
struct PiHoleTest: Codable {
let domains_being_blocked: String
let ads_blocked_today: String
}
let data = try! Data.init(contentsOf: URL.init(string: "http://pi.hole/admin/api.php?summary")!)
do {
let decoder: JSONDecoder = JSONDecoder.init()
let user: PiHoleTest = try decoder.decode(PiHoleTest.self, from: data)
print("In Blocklist \(user.domains_being_blocked)")
print("Blocked Today: \(user.ads_blocked_today) ")
} catch let e {
print(e)
}
The Output:
In Blocklist 1,089,374
Blocked Today: 11,258
What am I doing wrong? Or better, is there another way to fetch these stats?
Thanks in Advance!
The issue was related to the structure. Your JSON decoded were not an array. So PiHole struct was unnecessary. I can tested and this code is working now.
import SwiftUI
struct NetworkController {
static func fetchData(completion: #escaping ((Stat) -> Void)) {
if let url = URL(string: "http://pi.hole/admin/api.php?summary") {
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { (data, response, error) in
do {
if let data = data {
let stat = try JSONDecoder().decode(Stat.self, from: data)
DispatchQueue.main.async() {
completion(stat)
}
return
} else {
print("Error Found")
}
} catch let error as NSError {
print(error.localizedDescription)
}
}.resume()
}
}
}
class ContentViewModel: ObservableObject {
#Published var stat: Stat? = nil
func fetchData() {
NetworkController.fetchData { stat in
self.stat = stat
}
}
}
struct TestView: View {
#ObservedObject var viewModel = ContentViewModel()
var body: some View {
List {
Text(viewModel.stat?.domains_being_blocked ?? "No Data")
Text(viewModel.stat?.ads_blocked_today ?? "No Data")
Text(viewModel.stat?.ads_percentage_today ?? "No Data")
Text(viewModel.stat?.dns_queries_today ?? "No Data")
}.onAppear{
self.viewModel.fetchData()
}
}
}
struct Stat: Decodable, Hashable {
var domains_being_blocked: String
var ads_percentage_today: String
var ads_blocked_today: String
var dns_queries_today: String
}