how to fetch data from array by indexing in SwiftUI? - swiftui

This is my Json file and i don't understand how to fetch data and set
the Image in our SwiftUI code. please help me resolve this problem.
And this is My Model, is this model correct?
This is My API and wants to fetch only value images array
import Foundation
public struct BannerImages {
public let images: [String]
public init(images: [String]) {
self.images = images
}
}

try this approach to fetch your images and display them in a View:
import Foundation
import SwiftUI
struct ContentView: View {
#StateObject var vm = ViewModel()
var body: some View {
VStack {
Text("Fetching the data...")
List (vm.images, id: \.self) { url in
AsyncImage(url: URL(string: url)) { image in
image
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 111, height: 111)
} placeholder: {
ProgressView()
}
}
}
.task {
await vm.getData()
}
}
}
class ViewModel: ObservableObject {
#Published var images = [String]()
func getData() async {
guard let url = URL(string: "apiurl") else { return }
do {
let (data, _) = try await URLSession.shared.data(from: url)
Task{#MainActor in
let results = try JSONDecoder().decode(APIResponse.self, from: data)
self.images = results.images
}
} catch {
print("---> error: \(error)")
}
}
}
struct APIResponse: Codable {
let images: [String]
}

1. First you need to have a variable with data type of Data like this: var imageData: Data?
2. Then you have to fetch the image data from the link in the array like this:
func getImageData() {
// Check image url isnt nill
guard imageUrl(your image url) != nil else {
return
}
// Download the data for the image
let url = URL(string: imageUrl(your image url)!)
if let url = url {
let session = URLSession.shared
let dataTask = session.dataTask(with: url) { data, response, error in
if error == nil {
DispatchQueue.main.async {
self.imageData = data
}
}
}
dataTask.resume()
}
}
3. Once this is done go to the view file where you want to display the image and create
let uiImage = UIImage(data: put the var in which you stored the image data in previous step ?? Data())
Image(uiImage: uiImage ?? UIImage())

Related

Cant view the list of devices

I have a json-result that I get from my own "api". In this I have a list of different devices, that I like to view in a list.
When I add the ForEach, then I get the following error:
Generic struct 'List' requires that 'some AccessibilityRotorContent' conform to 'View'
The JsonResponse:
[{"name":"Tormek-T8","topHorizontal":55,"topVertical":55,"frontHorizontal":44,"frontVertical":44},{"name":"SH-332","topHorizontal":77,"topVertical":77,"frontHorizontal":88,"frontVertical":88}]
Can it be, that there are numbers in this JSON-String and not all is a String?
In my very simple code I had made a struct for the single Device and try to pull it from the url. Why I would get this error? Because the response of the JSON is not nil.
struct Device: Hashable, Codable {
let name: String
let topHorizontal: Double
let topVertical: Double
let frontHorizontal: Double
let frontVertical: Double
}
class ViewModel: ObservableObject {
#Published var devices: [Device] = []
func fetch() {
guard let url = URL(string: "https://cdn.rowoco.de/grindcalculator/devices") else {
return
}
let task = URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
guard let data = data, error == nil else {
return
}
do {
let devices = try JSONDecoder().decode([Device].self, from: data)
DispatchQueue.main.async {
self?.devices = devices
}
} catch {
print(error)
}
}
task.resume()
}
}
struct CdnDevicesListView: View {
#Environment(\.presentationMode) var presentationMode
#StateObject var viewModel = ViewModel()
var body: some View {
NavigationView {
List {
ForEach(viewModel.devices, id: \.self) { device in
device.name
}
}
.navigationTitle("cdn devices")
.onAppear {
viewModel.fetch()
}
}
}
}
ForEach's content needs to be a View.
device.name is not a View.

Upload SwiftUI Image to Firebase with Data

I'm creating simple CRUD operations in an app and I came across a hairy quirk, given SwiftUI's Image design.
I'm trying to upload an image to Firebase, except I need to convert an Image to a UIImage, and further into Data.
Here's my code:
Image Uploader
import UIKit
import Firebase
import FirebaseStorage
import SwiftUI
struct ImageUploader {
static func uploadImage(with image: Data, completion: #escaping(String) -> Void) {
let imageData = image
let fileName = UUID().uuidString
let storageRef = Storage.storage().reference(withPath: "/profile_images/\(fileName)")
storageRef.putData(imageData, metadata: nil) { (metadata, error) in
if let error = error {
print("Error uploading image to Firebase: \(error.localizedDescription)")
return
}
print("Image successfully uploaded to Firebase!")
}
storageRef.downloadURL { url, error in
guard let imageUrl = url?.absoluteString else { return }
completion(imageUrl)
}
}
}
View Model
import SwiftUI
import Firebase
func uploadProfileImage(with image: Data) {
guard let uid = tempCurrentUser?.uid else { return }
let imageData = image
ImageUploader.uploadImage(with: imageData) { imageUrl in
Firestore.firestore().collection("users").document(uid).updateData(["profileImageUrl":imageUrl]) { _ in
print("Successfully updated user data")
}
}
}
ImagePicker
import SwiftUI
import PhotosUI
#MainActor
class ImagePicker: ObservableObject {
#Published var image: Image?
#Published var imageSelection: PhotosPickerItem? {
didSet {
if let imageSelection {
Task {
try await loadTransferrable(from: imageSelection)
}
}
}
}
func loadTransferrable(from imageSelection: PhotosPickerItem?) async throws {
do {
if let data = try await imageSelection?.loadTransferable(type: Data.self) {
if let uiimage = UIImage(data: data) {
self.image = Image(uiImage: uiimage)
}
}
} catch {
print("Error: \(error.localizedDescription)")
}
}
}
View
if let image = imagePicker.image {
Button {
viewModel.uploadProfileImage(with: image)
} label: {
Text("Continue")
.font(.headline)
.foregroundColor(.white)
.frame(width: 340.0, height: 50.0)
.background(.blue)
.clipShape(Capsule())
.padding()
}
.shadow(radius: 10.0)
.padding(24.0)
}
As you can see, I have a problem in the view: Cannot convert value of type 'Image' to expected argument type 'Data', which makes sense. The images are all of type Data.
How can I do it?
don't use #Published var image: Image? in your class ImagePicker: ObservableObject, Image is a View and is for use in other Views.
Use #Published var image: UIImage? and adjust your code accordingly.
Then use image.pngData() to get the Data to upload.

AsyncImage not rendering all images in a List and producing error Code =-999 "cancelled"

I'm having trouble getting all images to show in a List using AsyncImage. When you first run the app, it seems fine but if you start scrolling, some Images aren't shown. Either the ProgressView doesn't update until the row is scrolled outside of the screen and back into view or I get an error Code=-999 "cancelled".
I can get the all the images to show and ProgressView to update correctly using a regular Image View and going through the whole setup process of downloading and showing an image. It's only when I try to use AsyncImage that all the images aren't shown. How can I get all AsyncImages to show in a List?
struct ContentView: View {
#StateObject private var viewModel = ListViewModel()
var body: some View {
List {
ForEach(viewModel.images) { photo in
HStack {
AsyncImage(url: URL(string: photo.thumbnailUrl)) { phase in
switch phase {
case .success(let image):
image
case .failure(let error):
let _ = print(error)
Text("error: \(error.localizedDescription)")
case .empty:
ProgressView()
#unknown default:
fatalError()
}
}
VStack(alignment: .leading) {
Text(photo.title)
Text(photo.thumbnailUrl)
}
}
}
}
.task {
await viewModel.loadImages()
}
}
}
#MainActor
class ListViewModel: ObservableObject {
#Published var images: [PhotoObject] = []
func loadImages() async {
do {
let photos = try await AsyncImageManager.downloadImages()
images = photos
} catch {
print("Could load photos: \(error)")
}
}
}
struct AsyncImageManager {
static func downloadImages() async throws -> [PhotoObject] {
let url = URL(string: "https://jsonplaceholder.typicode.com/photos")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode([PhotoObject].self, from: data)
}
}
struct PhotoObject: Identifiable, Codable {
let albumId: Int
let id: Int
let title: String
let url: String
let thumbnailUrl: String
}
This is still a bug in iOS 16.
The only solution that I found is to use ScrollView + LazyVStack instead of List.

How to loop through API call data and display in view

I know I can use for each for this, but every time I try to implement according to documentation it throws some kind of error regarding syntax.
Here is my view:
import SwiftUI
import Combine
struct HomeTab: View {
#StateObject var callDevices = CallDevices()
var body: some View {
NavigationView {
devices
.onAppear {
callDevices.getDevices()
}
}
}
private var devices: some View {
VStack(alignment: .leading, spacing: nil) {
ForEach(content: callDevices.getDevices(), id: \.self) { device in
// i want to loop through and display here //
HStack{
Text(device.Name)
Text(device.Status)
}
}
Spacer()
}
}
}
struct HomeTab_Previews: PreviewProvider {
static var previews: some View {
HomeTab()
}
}
Here is my Call Devices which works without issue in other views:
class CallDevices: ObservableObject {
private var project_id: String = "r32fddsf"
#Published var devices = [Device]()
func getDevices() {
guard let url = URL(string: "www.example.com") else {return}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("Authorization")
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil else {print(error!.localizedDescription); return }
// guard let data = data else {print("empty data"); return }
let theData = try! JSONDecoder().decode(Welcome.self, from: data!)
DispatchQueue.main.async {
self.devices = theData.devices
}
}
.resume()
}
}
is the issue in the way I am calling my function?
try this:
(you may need to make Device Hashable)
private var devices: some View {
VStack(alignment: .leading, spacing: nil) {
ForEach(callDevices.devices, id: \.self) { device in // <-- here
// i want to loop through and display here //
HStack{
Text(device.Name)
Text(device.Status)
}
}
Spacer()
}
}
If Device is Identifiable, you can remove the id: \.self.
struct Device: Identifiable, Hashable, Codable {
let id = UUID()
var Name = ""
var Status = ""
// ... any other stuff you have
}

SwiftUI 2.0: export group of images with .fileExporter modifier

This is a follow up on this thread:
SwiftUI 2.0: export images with .fileExporter modifier
Goal: export a group of images in SwiftUI
What I did:
I am using the .fileExporter modifier, with the FileDocument struct.
Also open to other approach, like . fileMover modifier for example.
Problem:
When setting the FileDocument for multiple images struct I am getting am error on func fileWrapper (check code bellow).
Question:
How can I export multiple images in SwiftUI (could be any method)?
//file exporter
.fileExporter(isPresented: $exportFile, document: ImageDocument(
image: UIImage(data: product.cover ?? Data())!,
image2: UIImage(data: product.cover2 ?? Data())!)
,
contentType: .jpeg, onCompletion: { (result) in
if case .success = result {
print("Success")
} else {
print("Failure")
}
})
//export group of images
struct ImageDocument: FileDocument {
static var readableContentTypes: [UTType] { [.jpeg] }
var image: UIImage
var image2: UIImage
init(
image: UIImage?,
image2: UIImage?
) {
self.image = image ?? UIImage()
self.image2 = image2 ?? UIImage()
}
init(configuration: ReadConfiguration) throws {
guard let data = configuration.file.regularFileContents,
let image = UIImage(data: data),
let image2 = UIImage(data: data)
else {
throw CocoaError(.fileReadCorruptFile)
}
self.image = image
self.image2 = image2
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
return FileWrapper(regularFileWithContents:
image.jpegData(compressionQuality: 0.80)!,
image2.jpegData(compressionQuality: 0.80)!//<----- getting an "extra argument error here
)
}
}
Update:
Tried to rewrite Max answer to UIImage, but this only exports 1 image:
import SwiftUI
class AppContext: ObservableObject {
#Published var fileSaveDialogShown = false
}
#main
struct FocalApp: App {
#StateObject var appContext = AppContext()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(self.appContext)
.fileExporter(
isPresented: $appContext.fileSaveDialogShown,
documents: [
ImageDocument(image: UIImage(named: "1")),
ImageDocument(image: UIImage(named: "2"))
],
contentType: .jpeg // Match this to your representation in ImageDocument
) { url in
print("Saved to", url) // [URL]
}
}
}
}
import SwiftUI
import UniformTypeIdentifiers
struct ImageDocument: FileDocument {
static var readableContentTypes: [UTType] { [.jpeg, .png, .tiff] }
var image: UIImage
init(image: UIImage?) {
self.image = image ?? UIImage()
}
init(configuration: ReadConfiguration) throws {
guard let data = configuration.file.regularFileContents,
let image = UIImage(data: data)
else {
throw CocoaError(.fileReadCorruptFile)
}
self.image = image
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
// You can replace tiff representation with what you want to export
return FileWrapper(regularFileWithContents: image.jpegData(compressionQuality: 1)!)
}
}
struct ContentView: View {
#EnvironmentObject var appContext: AppContext
var body: some View {
VStack {
Button(action: {
appContext.fileSaveDialogShown.toggle()
}, label: {
Text("Button")
})
}
.frame(width: 200, height: 200)
}
}
You need to use fileExporter with documents instead of document, which takes in a Collection
Here's how I'm doing it on macOS, adapting it should be straightforward:
import SwiftUI
import UniformTypeIdentifiers
struct ImageDocument: FileDocument {
static var readableContentTypes: [UTType] { [.jpeg, .png, .tiff] }
var image: NSImage
init(image: NSImage?) {
self.image = image ?? NSImage()
}
init(configuration: ReadConfiguration) throws {
guard let data = configuration.file.regularFileContents,
let image = NSImage(data: data)
else {
throw CocoaError(.fileReadCorruptFile)
}
self.image = image
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
// You can replace tiff representation with what you want to export
return FileWrapper(regularFileWithContents: image.tiffRepresentation!)
}
}
#main
struct FocalApp: App {
#StateObject var appContext = AppContext()
var body: some Scene {
WindowGroup {
MainView()
.environmentObject(self.appContext)
.fileExporter(
isPresented: $appContext.fileSaveDialogShown,
documents: [
ImageDocument(image: NSImage(named: "testimage1")),
ImageDocument(image: NSImage(named: "testimage2"))
],
contentType: .tiff // Match this to your representation in ImageDocument
) { url in
print("Saved to", url) // [URL]
}
}
}
}