Download Image from POST Request using any third party SwiftUI - image

in my SwiftUI app i am storing images to IPFS Data Store and they have recently made the url as a POST request from GET request. In that case the third party library is not able to render the image. How can i resolve this?
This is the example url i am trying to render
link to the image
This is the exact error i get:
Error Domain=SDWebImageErrorDomain Code=2001 "Download marked as failed because of invalid response status code 405" UserInfo={SDWebImageErrorDownloadResponseKey=<NSHTTPURLResponse: 0x600000ed8340> { URL: https://ipfs.infura.io:5001/api/v0/cat?arg=QmVHLDEY4DhzJeW7G47QgpdibU6QX8g8He6CS83qRbpcDB } { Status Code: 405, Headers {
"Content-Length" = (
28
);
"Content-Type" = (
"text/plain; charset=utf-8"
);
Date = (
"Thu, 03 Mar 2022 17:20:01 GMT"
);
Vary = (
Origin
);
"X-Content-Type-Options" = (
nosniff
);
"X-Robots-Tag" = (
noindex
);
} }, NSLocalizedDescription=Download marked as failed because of invalid response status code 405, SDWebImageErrorDownloadStatusCodeKey=405}
My code snippet for SDWebImageSwiftUI:
ForEach(vm.item.tabImagesArray.indices, id: .self) { index in
WebImage(url: URL(string: vm.item.tabImagesArray[index]))
.resizable()
.placeholder {
Image("placeholder")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
.scaledToFit()
}
.onFailure { error in
print(error)
}
.onSuccess { img, data, _ in
print(img)
print(data)
}
.transition(.fade(duration: 0.5))
.frame(height: vm.index == index ? 200 : 150)
.cornerRadius(15)
.padding(.horizontal)
.tag(index)
.onTapGesture {
self.selectedImage = vm.item.tabImagesArray[index]
self.showImage.toggle()
}
}

Related

Replace images with thumbnail on failed - swiftUI

I am fetching images from Firebase storage, if there is no image on firebase, I want to show thumbnail,
Thats where I get the error
if let error = error {
Swift.print(error)
}
Here is my thumbnail
Image("shoePlaceHolder")
.resizable()
.aspectRatio(contentMode: .fit)
Here is the complete code
func getFullImageURL() {
let storage = Storage.storage()
let storagePath = "gs://on-switch.appspot.com/main/\(season)/"
let storageRef = storage.reference(forURL: storagePath)
let formattedImageURL = imageURL.absoluteString.replacingOccurrences(of: "file:///", with: "")
let ref = storageRef.child(formattedImageURL)
ref.downloadURL { url, error in
DispatchQueue.main.async {
if let error = error {
Swift.print(error)
} else if let url = url {
fullImageURL = url
} else {
Swift.print("No url and no error")
}
}
}
}
#ViewBuilder
var content: some View {
VStack {
VStack {
headerView()
HStack {
AsyncImage(url: $fullImageURL.wrappedValue) { image in
image
.resizable()
.aspectRatio(contentMode: .fit)
} placeholder: {
Image("shoePlaceHolder")
.resizable()
.aspectRatio(contentMode: .fit)
}.frame(width: 260, height: 180)
.onAppear(perform: getFullImageURL)
VStack(alignment: .leading) {
titleView()
subtitleView()
}
Spacer()
}
}
.padding()
OnDivider()
.padding(.horizontal)
SectionView(leadingView: {
Text("\(variants.count) variants")
.secondaryText()
}, trailingView: {
Image(systemName: "rectangle.expand.vertical")
.foregroundColor(Color(.secondaryLabel))
}).padding()
}
You have to use this line to invoke the listener for fullImageURL
fullImageURL = nil
and the complete code will look like this:
func getFullImageURL() {
let storage = Storage.storage()
let storagePath = "gs://on-switch.appspot.com/main/\(season)/"
let storageRef = storage.reference(forURL: storagePath)
let formattedImageURL = imageURL.absoluteString.replacingOccurrences(of: "file:///", with: "")
let ref = storageRef.child(formattedImageURL)
ref.downloadURL { url, error in
DispatchQueue.main.async {
if let error = error {
Swift.print(error)
fullImageURL = nil
} else if let url = url {
fullImageURL = url
} else {
Swift.print("No url and no error")
fullImageURL = nil
}
}
}
}
Since no image was shown as an error, you just need to focus on the error. Something like:
if error.localizedDescription == NoImageErrorString { // NoImageErrorString is something you can find in Firebase code
// show thumbnail here
} else {
// your original code
}
This works for me
struct ContentView: View {
#State private var url: URL?
func getFullImageURL() {
self.url = URL(string: "")
}
var body: some View {
VStack {
AsyncImage(url: url) { image in
image
.resizable()
.aspectRatio(contentMode: .fit)
} placeholder: {
Image(systemName: "star")
.resizable()
.aspectRatio(contentMode: .fit)
}
.frame(width: 260, height: 180)
.onAppear(perform: getFullImageURL)
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
If you want to use a custom image, make sure shoePlaceHolder is added to the project Assets.
Image("shoePlaceHolder")
.
You can use a struct instead of a #State, #Published or whatever you're using to store fullImageURL.
Something like:
struct ImageWithErrorPlaceholder {
var fullImageURL: URL?
var showErrorPlaceholder: Bool = false
}
So you can simply inform the URL and the error as needed:
ref.downloadURL { url, error in
DispatchQueue.main.async {
if let error = error {
imageWithErrorPlaceholder.showErrorPlaceholder = true
Swift.print(error)
} else if let url = url {
imageWithErrorPlaceholder.fullImageURL = url
} else {
Swift.print("No url and no error")
}
}
}
That way you can display a different placeholder in case of error or no placeholder at all, depending or your needs:
AsyncImage(url: imageWithErrorPlaceholder.fullImageURL) { image in
image
.resizable()
.aspectRatio(contentMode: .fit)
} placeholder: {
if imageWithErrorPlaceholder.showErrorPlaceholder {
Image("shoePlaceHolder")
.resizable()
.aspectRatio(contentMode: .fit)
}
}

SwiftUI, CloudKit and Images

I'm really stumped by something I think that should be relatively easy, so i need a little bump in the right direction. I've searched in a lot of places and I get either the wrong information, or outdated information (a lot!).
I am working with Core Data and CloudKit to sync data between the user's devices. Images I save as CKAsset attached to a CKRecord. That works well. The problem is with retrieving the images. I need the images for each unique enitity (Game) in a list. So I wrote a method on my viewModel that retrieves the record with the CKAsset. This works (verified), but I have no idea how to get the image out and assign that to a SwiftUI Image() View. My current method returns a closure with a UIImage, how do I set that image to an Image() within a foreach. Or any other solution is appreciated. Musn't be that hard to get the image?
/// Returns the saved UIImage from CloudKit for the game or the default Image!
func getGameImageFromCloud(for game: Game, completion: #escaping (UIImage) -> Void ) {
// Every game should always have an id (uuid)!
if let imageURL = game.iconImageURL {
let recordID = CKRecord.ID(recordName: imageURL)
var assetURL = ""
CKContainer.default().privateCloudDatabase.fetch(withRecordID: recordID) { record, error in
if let error = error {
print(error.getCloudKitError())
return
} else {
if let record = record {
if let asset = record["iconimage"] as? CKAsset {
assetURL = asset.fileURL?.path ?? ""
DispatchQueue.main.async {
completion(UIImage(contentsOfFile: assetURL) ?? AppImages.gameDefaultImage)
}
}
}
}
}
} else {
completion(AppImages.gameDefaultImage)
}
}
This is the ForEach I want to show the Image for each game (but this needed in multiple places:
//Background Tab View
TabView(selection: $gamesViewModel.currentIndex) {
ForEach(gamesViewModel.games.indices, id: \.self) { index in
GeometryReader { proxy in
Image(uiImage: gamesViewModel.getGameImageFromCloud(for: gamesViewModel.games[index], completion: { image in
}))
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: proxy.size.width, height: proxy.size.height)
.cornerRadius(1)
}
.ignoresSafeArea()
.offset(y: -100)
}
.onAppear(perform: loadImage)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.animation(.easeInOut, value: gamesViewModel.currentIndex)
.overlay(
LinearGradient(colors: [
Color.clear,
Color.black.opacity(0.2),
Color.white.opacity(0.4),
Color.white,
Color.systemPurple,
Color.systemPurple
], startPoint: .top, endPoint: .bottom)
)
.ignoresSafeArea()
TIA!
So, let's go... extract ForEach image dependent internals into subview, like (of course it is not testable, just idea):
ForEach(gamesViewModel.games.indices, id: \.self) { index in
GeometryReader { proxy in
GameImageView(model: gamesViewModel, index: index) // << here !!
.frame(width: proxy.size.width, height: proxy.size.height)
.cornerRadius(1)
//.onDisappear { // if you think about cancelling
// gamesViewModel.cancelLoad(for: index)
//}
}
.ignoresSafeArea()
.offset(y: -100)
}
.onAppear(perform: loadImage)
and now subview itself
struct GameImageView: View {
var model: Your_model_type_here
var index: Int
#State private var image: UIImage? // << here !!
var body: some View {
Group {
if let loadedImage = image {
Image(uiImage: loadedImage) // << here !!
.resizable()
.aspectRatio(contentMode: .fill)
} else {
Text("Loading...")
}
}.onAppear {
model.getGameImageFromCloud(for: model.games[index]) { image in
self.image = image
}
}
}
}
For completion's sake, my own version:
struct GameImage: View {
var game: Game
#EnvironmentObject var gamesViewModel: GamesView.ViewModel
#State private var gameImage: UIImage?
var body: some View {
Group {
if let gameImage = gameImage {
Image(uiImage: gameImage)
.resizable()
.aspectRatio(contentMode: .fill)
} else {
ZStack(alignment: .center) {
Image(uiImage: AppImages.gameDefaultImage)
.resizable()
.aspectRatio(contentMode: .fill)
ProgressView()
.foregroundColor(.orange)
.font(.title)
}
}
}.onAppear {
gamesViewModel.getGameImageFromCloud(for: game) { image in
self.gameImage = image
}
}
}
}

How to change focus in GridView on key navigation QML

I'm trying to change focus of an image while pressing keys (left, right, up and down) in GridView. When I press left, right, up or down key, I should change image focus and change the image when focus on that image is true. Otherwise, if focus is not on the image, old image should be seen.
Here is what I have by now:
And here is my code:
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 1920
height: 1080
visible: true
title: qsTr("Hello World")
Component.onCompleted: {
mojgrid.focus = true
}
function dobioResponseNapraviModel(response) {
console.log("dobioResponseNapraviModel", typeof response)
mojgrid.model=response
}
function request(){
console.log("BOK")
const xhr=new XMLHttpRequest()
const method="GET";
const url="http://api.themoviedb.org/4/list/1";
xhr.open(method, url, true);
xhr.setRequestHeader( "Authorization", 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI5YjBkOGVlMGQzODdiNjdhYTY0ZjAzZDllODM5MmViMyIsInN1YiI6IjU2MjlmNDBlYzNhMzY4MWI1ZTAwMTkxMyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.UxgW0dUhS62m41KjqEf35RWfpw4ghCbnSmSq4bsB32o');
xhr.onreadystatechange=function(){
if(xhr.readyState===XMLHttpRequest.DONE){
var status=xhr.status;
if(status===0 || (status>=200 && status<400)){
//the request has been completed successfully
// console.log(xhr.responseText.results)
dobioResponseNapraviModel(JSON.parse(xhr.responseText).results)
}else{
console.log("There has been an error with the request", status, JSON.stringify(xhr.responseText))
}
}
}
xhr.send();
}
/* function request(url, callback) {
var xhr=new XMLHttpRequest();
xhr.open("GET", url, true)
xhr.onreadystatechange = function() {
if(xhr.readyState===4) {
callback(xhr.responseText)
}
}
xhr.open("GET", url)
xhr.setRequestHeader( "Authorization", 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI5YjBkOGVlMGQzODdiNjdhYTY0ZjAzZDllODM5MmViMyIsInN1YiI6IjU2MjlmNDBlYzNhMzY4MWI1ZTAwMTkxMyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.UxgW0dUhS62m41KjqEf35RWfpw4ghCbnSmSq4bsB32o');
xhr.send()
}*/
GridView {
id:mojgrid
anchors.fill: parent
cellWidth: 250
cellHeight: 250
model:request()
currentIndex: modelData.id
keyNavigationEnabled: true
focus:true
Keys.onPressed:{
if((event.key === Qt.Key_Left) || (event.key===Qt.Key_Right) || (event.key===Qt.Key_Up) || (event.key===Qt.Key_Down)){
image.source="http://image.tmdb.org/t/p/w400"+modelData.poster_path
}
}
/* Keys.onUpPressed: {
request()
}*/
delegate: Rectangle{ id: rect; width: 350; height: 400; color:'gray';
Image{id:img; width:parent.width; height:parent.height-200
//fillMode: Image.PreserveAspectFit
//source:"http://image.tmdb.org/t/p/w400" + modelData.backdrop_path
source:focus?"http://image.tmdb.org/t/p/w400"+modelData.poster_path : "http://image.tmdb.org/t/p/w400" + modelData.backdrop_path
Rectangle{
id:rect2
width:parent.width
height:text.height
anchors.top:img.bottom
color:'black'
Text{
id:text
text:modelData.title
font.pointSize: 11
//anchors.top:image.bottom
elide:Text.ElideNone
color:'white'
}
}
MouseArea{
id:mouse
anchors.fill:parent
onClicked: {
parent.focus=true
}
}
}
Rectangle{
id:rect3
width:parent.width
height:200
anchors.top:rect.bottom
color:'red'
z:10
Text{
text:modelData.release_date
anchors.left:rect.left
anchors.top:rect.bottom
color: 'white'
}
}
}
}
}
I have Keys.onPressed here with an if condition, but it doesn't work. Can someone help me?
I simplified and reformat your code little bit but this code snippet is doing what do you want to do. When key navigation is enabled GridView is handling index update by itself. Actually key navigation is working and when you press keys current index is updated. GridView also handles limits on navigation, when you press down in the last row nothing happens as same as when you press left on the first column. The trick is using currentindex to update image.
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
width: 1920
height: 1080
visible: true
title: qsTr("Hello World")
function dobioResponseNapraviModel(response) {
console.log("dobioResponseNapraviModel", typeof response)
mojgrid.model = response
}
GridView {
id: mojgrid
anchors.fill: parent
cellWidth: 250
cellHeight: 250
model: request()
keyNavigationEnabled: true
focus: true
delegate: Rectangle {
id: rect
width: 350
height: 400
color: 'gray'
property bool isCurrent: mojgrid.currentIndex === index
Image {
id: img
width: parent.width
height: parent.height - 200
source: isCurrent ? "http://image.tmdb.org/t/p/w400"
+ modelData.poster_path : "http://image.tmdb.org/t/p/w400"
+ modelData.backdrop_path
Rectangle {
id: rect2
width: parent.width
height: text.height
anchors.top: img.bottom
color: 'black'
Text {
id: text
text: modelData.title
font.pointSize: 11
//anchors.top:image.bottom
elide: Text.ElideNone
color: 'white'
}
}
MouseArea {
id: mouse
anchors.fill: parent
onClicked: {
mojgrid.currentIndex = index
}
}
}
}
}
function request() {
console.log("BOK")
const xhr = new XMLHttpRequest()
const method = "GET"
const url = "http://api.themoviedb.org/4/list/1"
xhr.open(method, url, true)
xhr.setRequestHeader(
"Authorization",
'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI5YjBkOGVlMGQzODdiNjdhYTY0ZjAzZDllODM5MmViMyIsInN1YiI6IjU2MjlmNDBlYzNhMzY4MWI1ZTAwMTkxMyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.UxgW0dUhS62m41KjqEf35RWfpw4ghCbnSmSq4bsB32o')
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE) {
var status = xhr.status
if (status === 0 || (status >= 200 && status < 400)) {
dobioResponseNapraviModel(JSON.parse(
xhr.responseText).results)
} else {
console.log("There has been an error with the request",
status, JSON.stringify(xhr.responseText))
}
}
}
xhr.send()
}
}

Cannot find "Resize" in Scope, SwiftUI for dummies problem please

Hi following is my code for the News Reader app exercise in swiftui for dummies, I'm getting an error "Cannot find Resize in Scope" any help will be appreciated
Code:
"
import SwiftUI
import URLImage
import URLImageStore
struct Result: Codable {
var articles: [Article]
}
struct Article: Codable {
var url: String
var title: String
var description: String?
var urlToImage: String?
}
struct ContentView: View {
private let url = "https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=49d5bfa113c34ec0af781fab38395996"
#State private var articles = [Article]()
func fetchData() {
guard let url = URL(string: url) else {
print("URL is Not Valid")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) {
data, response, error in
if let data = data {
if let decodedResult = try?
JSONDecoder().decode(
Result.self, from: data) {
DispatchQueue.main.async {
self.articles = decodedResult.articles
}
return
}
}
print ("Error: \(error?.localizedDescription ?? "Unknown Error")")
}.resume()
}
var body: some View {
List(articles, id: \.url) { item in
HStack(alignment: .top) {
U**RLImage(
(( URL(string:item.urlToImage ?? "https://picsum.photos/100")
?? nil
)!),
delay: 0.25,
processors:
[Resize(size: CGSize(width: 100.0, height: 100.0), scale: UIScreen.main.scale)],
content: {
$0.image
.resizable()
.aspectRatio(contentMode: .fit)
.clipped()
}
).frame(width: 100.0, height: 100.0)**
VStack(alignment: .leading) {
Text(item.title)
.font(.headline)
Text(item.description ?? "")
.font(.footnote)
}
}
}.onAppear(perform: fetchData)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
"
Please help in resolving my problem the problem is I'm getting cannot find "resize" in scope. so the question is: how can I mitigate this problem?
the error is telling you that you are missing the class or struct called Resize. Find where that is in the excercise and put it in a file in your project or in your code above. Most likely at the error is at:
"processors:
[Resize(size: ....." // <--- here
Note there is something strange about the name " U**RLImage". It should probably be "URLImage"

Update shape color using Ruby Google Slides API

I'm trying to update an ellipsis shape using the google slides api in ruby. This is the code:
shape_properties = {
shape_background_fill: {
solid_fill: {
color: {
rgb_color: {
red: 1.0,
green: 0,
blue: 0
}
}
}
}
}
requests = [{
update_shape_properties: {
object_id: ellipse.object_id,
fields: 'shapeBackgroundFill',
shape_properties: shape_properties,
},
}]
# Execute the request.
req = Google::Apis::SlidesV1::BatchUpdatePresentationRequest.new(requests: requests)
response = #slides.batch_update_presentation(presentation_id,req)
Another code that I've tried with the same error is this one:
rgb_color = Google::Apis::SlidesV1::RgbColor.new(red: 1.0, green: 0, blue: 0)
color = Google::Apis::SlidesV1::OpaqueColor.new(rgb_color: rgb_color)
solid_fill = Google::Apis::SlidesV1::SolidFill.new(color: color)
shape_background_fill = Google::Apis::SlidesV1::ShapeBackgroundFill.new(solid_fill: solid_fill)
shape_properties = Google::Apis::SlidesV1::ShapeProperties.new(shape_background_fill: shape_background_fill)
requests = [{
update_shape_properties: {
object_id: ellipse.object_id,
fields: 'shapeBackgroundFill',
shape_properties: shape_properties,
},
}]
req = Google::Apis::SlidesV1::BatchUpdatePresentationRequest.new(requests: requests)
response = #slides.batch_update_presentation(presentation_id, req)
I get this error:
`check_status': badRequest: Invalid requests[0].updateShapeProperties: The object () could not be found. (Google::Apis::ClientError)
Any idea why is it fails?
try using object_id_prop instead of object_id in update_shape_properties.
shape_properties = {
shape_background_fill: {
solid_fill: {
color: {
rgb_color: {
red: 1.0,
green: 0,
blue: 0
}
}
}
}
}
requests = [{
update_shape_properties: {
object_id_prop: ellipse.object_id,
fields: 'shapeBackgroundFill',
shape_properties: shape_properties,
},
}]
# Execute the request.
req = Google::Apis::SlidesV1::BatchUpdatePresentationRequest.new(requests:
requests)
response = #slides.batch_update_presentation(pres`entation_id,req)
Because UpdateShapePropertiesRequest has object_id_prop accessor instead of object_id.
That's why object_id name is already used in Object.

Resources