IOS Widget is not updating - ios

I have an Quote app made in Flutter and now i create widget for IOS. My Widget loads some data from UserDefaults/ Appgroups and depending on that it shows some quotes and a picture. This works with the first start when i press button to set quote and images.
I want the widget to show new quotes every minute, tried different approaches with the Timeline, but it is not working. Can anyone help me make this works?
Below the code for the widget:
func placeholder(in context: Context) -> ExampleEntry {
ExampleEntry(date: Date(), flutterData: FlutterData( title: "Placeholder Title", message: "Placeholder Message",imageId:"tree", textColor:"Color.black"))
}
func getSnapshot(in context: Context, completion: #escaping (ExampleEntry) -> ()) {
let data = UserDefaults.init(suiteName:"***")
let entry = ExampleEntry(date: Date(),flutterData: FlutterData( title: data?.string(forKey: "title") ?? "Set quote and background in Settings > Widget", message: data?.string(forKey: "message") ?? "No Message Set", imageId: data?.string(forKey: "imageId") ?? "Color.black", textColor: data?.string(forKey: "textColor") ?? "Color.white"))
completion(entry)
}
func getTimeline(in context: Context, completion: #escaping (Timeline<ExampleEntry>) -> ()) {
let data = UserDefaults.init(suiteName:"***")
var entries = [ExampleEntry]()
let currentDate = Date()
let entry = ExampleEntry(date: currentDate,flutterData:FlutterData(title: data?.string(forKey: "title") ?? "Set quote and background in Settings > Widget", message: data?.string(forKey: "message") ?? "No Message Set", imageId: data?.string(forKey: "imageId") ?? "Color.black", textColor: data?.string(forKey: "textColor") ?? "Color.white"))
let entryDate = Calendar.current.date(byAdding: .minute, value: 1, to: currentDate)
print(entryDate)
let timeline = Timeline(entries: [entry], policy: .after(entryDate!))
completion(timeline)
}
}
var body: some View {
VStack.init(alignment: .leading, spacing: nil, content: {
Text(entry.flutterData!.title)
.font(.body)
.foregroundColor(Color.white)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: Alignment.center)
.padding(.vertical)
.padding(.horizontal,6)
.background(
Image(entry.flutterData!.imageId)
.resizable()
.scaledToFill()
.frame( maxWidth: .infinity, maxHeight: .infinity)
)
.onReceive(NotificationCenter.default.publisher(for: .NSPersistentStoreRemoteChange)) { _ in
// make sure you don't call this too often
WidgetCenter.shared.reloadAllTimelines()
}
}
)
}
}
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind,provider: Provider()) { entry in
HomeWidgetExampleEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}

Related

Widget element disappears in Widget Gallery - Swift

I have a weird issue I'm not sure how to resolve. I have a weather app that displays Celsius and Fahrenheit at the same time, as well as an SF symbol for the weather condition.
Everything in the widget works perfectly on it's own. However, in the widget gallery, the SF symbol will disappear from the widget after about half a second and I have no idea why.
Here's all the code from the widget. It's my first attempt at one, so it's not very clean:
import WidgetKit
import SwiftUI
let otherUserDefaults = UserDefaults(suiteName: "group.DualTemp")
let temperatureCelsius = otherUserDefaults?.value(forKey: "CTempWidget") ?? 0
let temperatureFahrenheit = otherUserDefaults?.value(forKey: "FTempWidget")
let name = otherUserDefaults?.value(forKey: "SearchedCityWidget")
let weatherID = otherUserDefaults?.string(forKey: "WidgetIcon")
let weather = UIImage(systemName: weatherID!)
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), name: "", temperatureCelsius: "0°C", temperatureFahrenheit: "0°F", weatherName: "Cloud", weatherIcon: weather!)
}
func getSnapshot(in context: Context, completion: #escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), name: name as! String , temperatureCelsius: temperatureCelsius as! String, temperatureFahrenheit: temperatureFahrenheit as! String, weatherName: "sun", weatherIcon: weather!)
completion(entry)
}
func getTimeline(in context: Context, completion: #escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
let userDefaults = UserDefaults(suiteName: "group.DualTemp")
let temperatureCelsius = userDefaults?.value(forKey: "CTempWidget") ?? 0
let temperatureFahrenheit = userDefaults?.value(forKey: "FTempWidget")
let name = userDefaults?.value(forKey: "SearchedCityWidget")
let weatherName = userDefaults?.string(forKey: "WidgetIcon")
let weatherImage = UIImage(systemName: weatherName!)
//MARK: - refresh info
let totalCountdown = 30
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
for offset in 0 ..< totalCountdown {
let entryDate = Calendar.current.date(byAdding: .minute, value: offset, to: currentDate)!
let entry = SimpleEntry(date: entryDate, name: name as? String ?? "No location", temperatureCelsius: temperatureCelsius as? String ?? "0°C", temperatureFahrenheit: temperatureFahrenheit as? String ?? "0°F", weatherName: weatherName!,
weatherIcon: weatherImage!)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let name: String
let temperatureCelsius: String
let temperatureFahrenheit: String
let weatherName: String
let weatherIcon: UIImage
}
struct DualTempWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
ZStack{
Color("WidgetBackground")
VStack(spacing: 0.5) {
if #available(iOSApplicationExtension 16.0, *) {
Text(entry.name)
.padding(6)
.bold()
.font(.system(size: 27))
.minimumScaleFactor(0.01)
.lineLimit(1)
} else {
Text(entry.name)
.padding(6)
.font(.system(size: 29))
.minimumScaleFactor(0.01)
.lineLimit(1)
// Fallback on earlier versions
}
Image(systemName: entry.weatherName)
.resizable()
.symbolRenderingMode(.palette)
.scaledToFit()
.frame(width: 70, height: 70)
HStack{
Text(entry.temperatureCelsius)
.font(.system(size: 22))
Text("|")
Text(entry.temperatureFahrenheit)
.font(.system(size: 22))
}
}
}
}
}
struct DualTempWidget: Widget {
let kind: String = "DualTempWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
DualTempWidgetEntryView(entry: entry)
}
.configurationDisplayName("Dual Temp")
.description("See the current temperature, in celsius and fahrenheit, and weather conditions for your location.")
.supportedFamilies([.systemSmall])
}
}
struct DualTempWidget_Previews: PreviewProvider {
static var previews: some View {
DualTempWidgetEntryView(entry: SimpleEntry(date: Date(), name: name as! String , temperatureCelsius: temperatureCelsius as! String, temperatureFahrenheit: temperatureFahrenheit as! String, weatherName: weatherID!, weatherIcon: weather!))
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
Everything looks as it should be as everything in the widget updates correctly when you search for a new place in the app itself. Weird.

Implementing In App Purchases in IOS Widget

I'm currently stuck trying to implement in app purchases within my IOS Widget. My widget works, the in-app purchase works, and the app works for the baseline functions.
I am unable to make the purchases available within the widget. My current strategy is to use if else statements to turn change with parts of code to point to.
Could anyone tell me how to implement in app purchases within my Widget?
Here is my code:
code
import WidgetKit
import SwiftUI
struct Provider: TimelineProvider {
let userDefaults = UserDefaults(suiteName: "group.QuotePackage")
func placeholder(in context: Context) -> SimpleEntry {
_ = Quote.self
if UserDefaults.standard.bool(forKey: "premiumQuotes") == true {
// remove adds
return SimpleEntry(date: Date(), quoteDetails: QuoteProvider.random())
} else {
return SimpleEntry(date: Date(), quoteDetails: QuoteProvider2.random2())
}
}
//let userDefaults = UserDefaults(suiteName: "group.QuotePackage")
public typealias Entry = SimpleEntry
public func getSnapshot(in context: Context, completion: #escaping (SimpleEntry) -> ()) {
if UserDefaults.standard.bool(forKey: "premiumQuotes") == true {
// remove adds
let entry = SimpleEntry(date: Date(), quoteDetails: QuoteProvider.random())
} else {
let entry = SimpleEntry(date: Date(), quoteDetails: QuoteProvider2.random2())
}
completion(entry)
}
public func getTimeline(in context: Context, completion: #escaping (Timeline<Entry>) -> Void) {
var entries: [SimpleEntry] = []
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
if UserDefaults.standard.bool(forKey: "premiumQuotes") == true {
// remove adds
let entry = SimpleEntry(date: entryDate, quoteDetails: QuoteProvider.random())
} else {
let entry = SimpleEntry(date: entryDate, quoteDetails: QuoteProvider2.random2())
}
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct Quote_WidgetEntryView: View {
var entry: Provider.Entry
var body: some View {
QuoteWidgetEntryView(quoteDetails: entry.quoteDetails)
}
}
struct SimpleEntry: TimelineEntry {
public let date: Date
public let quoteDetails: QuoteDetails
}
struct PlaceholderView : View {
var body: some View {
Text("Inspo")
}
}
struct QuoteWidgetEntryView : View {
let quoteDetails: QuoteDetails
var body: some View {
ZStack {
Image("background")
.resizable()
.aspectRatio(contentMode: .fill)
VStack (alignment: .center, spacing: 15) {
Text("\"\(quoteDetails.texts)\"")
.font(Font.custom("Georgia", size: 20))
.fontWeight(.light)
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.lineLimit(5)
.padding(.leading, 20)
.padding(.trailing, 20)
Text("\(quoteDetails.authors)")
.font(Font.custom("Georgia", size: 15))
.fontWeight(.light)
.foregroundColor(Color.white)
}
}
}
}
#main
struct Quote2_Widget: Widget {
private let kind: String = "Quote2_Widget"
public var body: some WidgetConfiguration {
if UserDefaults.standard.bool(forKey: "premiumQuotes") == true {
// remove adds
StaticConfiguration(kind: kind, provider: Provider()) { entry in
QuoteWidgetEntryView(quoteDetails: QuoteProvider.random())
} else {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
QuoteWidgetEntryView(quoteDetails: QuoteProvider.random())
}
}
.configurationDisplayName("Inspo")
.description("Add Inspo Quotes to your homescreen.")
.supportedFamilies([.systemMedium])
}
}
}
struct QuoteWidgetView_Previews: PreviewProvider {
static var previews: some View {
QuoteWidgetEntryView(quoteDetails: QuoteProvider.random())
.previewContext(WidgetPreviewContext(family: .systemMedium))
}
}

SwiftUI Widget empty when built onto device or simulator

Playing with SwiftUI and WidgetKit recently and faced a nasty problem. My widget seems to be working in the SwiftUI Canvas but it is completely empty when built onto a simulator or device.
Images:
In SwiftUI canvas:
https://github.com/beanut/images/blob/main/Screenshot%202020-12-19%20at%204.00.57%20PM.png?raw=true
When built onto the device:
https://github.com/beanut/images/blob/main/Screenshot%202020-12-19%20at%204.01.13%20PM.png?raw=true
My code:
'''
import WidgetKit
import SwiftUI
import Intents
struct Provider: IntentTimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date())
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: #escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date())
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: #escaping (Timeline<Entry>) -> ()) {
var entries = [SimpleEntry]()
let currentDate = Date()
let midnight = Calendar.current.startOfDay(for: currentDate)
let nextMidnight = Calendar.current.date(byAdding: .day, value: 1, to: midnight)!
//To refresh timeline every min
for offset in 0 ..< 60 * 24 {
let entryDate = Calendar.current.date(byAdding: .minute, value: offset, to: midnight)!
entries.append(SimpleEntry(date: entryDate))
}
let timeline = Timeline(entries: entries, policy: .after(nextMidnight))
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
}
struct WidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
HStack() {
VStack {
Spacer()
VStack(alignment: .leading) {
Text(DateManager().getDayOfWeekInString(date: entry.date)!)
.font(Font(UIFont(name: "HoeflerText-Italic", size: 44)!))
.foregroundColor(.white)
.multilineTextAlignment(.trailing)
.opacity(1)
Text("\(DateManager().getDayAsString(date: entry.date)) \(DateManager().monthAsString(date: entry.date))")
.font(Font(UIFont(name: "Copperplate", size: 26)!))
.multilineTextAlignment(.trailing)
.opacity(1)
.foregroundColor(.gray)
}
}
.padding(.leading, 20)
.padding(.bottom, 20)
Spacer()
VStack(alignment: .trailing, spacing: -20) {
Text(DateManager().getHour(date: entry.date))
.font(Font(UIFont(name: "Copperplate-Bold", size: 86)!))
.foregroundColor(.white)
.multilineTextAlignment(.trailing)
.opacity(1)
Text(DateManager().getMinuteWithTwoDigits(date: entry.date))
.font(Font(UIFont(name: "Copperplate-Bold", size: 86)!))
.multilineTextAlignment(.trailing)
.opacity(1)
.foregroundColor(.gray)
}
.padding(.trailing, 15)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Image(uiImage: #imageLiteral(resourceName: "moon")), alignment: .center)
}
}
#main
struct Widget: SwiftUI.Widget {
let kind: String = "Widget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
WidgetEntryView(entry: entry)
}
.configurationDisplayName("TestWidget")
.description("This is an example widget.")
}
}
struct Widget_Previews: PreviewProvider {
static var previews: some View {
WidgetEntryView(entry: SimpleEntry(date: Date()))
.previewContext(WidgetPreviewContext(family: .systemMedium))
}
}
'''
**Update
I noticed that the widget does show if I remove a VStack. But it doesn't show if I add the code back.
Removing the VStack:
struct WidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
HStack() {
// VStack {
// Spacer()
// VStack(alignment: .leading) {
// Text(DateManager().getDayOfWeekInString(date: entry.date)!)
// .font(Font(UIFont(name: "HoeflerText-Italic", size: 44)!))
// .foregroundColor(.white)
// .multilineTextAlignment(.trailing)
// .opacity(1)
// Text("\(DateManager().getDayAsString(date: entry.date)) \(DateManager().monthAsString(date: entry.date))")
// .font(Font(UIFont(name: "Copperplate", size: 26)!))
// .multilineTextAlignment(.trailing)
// .opacity(1)
// .foregroundColor(.gray)
// }
// }
// .padding(.leading, 20)
// .padding(.bottom, 20)
Spacer()
VStack(alignment: .trailing, spacing: -20) {
Text(DateManager().getHour(date: entry.date))
.font(Font(UIFont(name: "Copperplate-Bold", size: 86)!))
.foregroundColor(.white)
.multilineTextAlignment(.trailing)
.opacity(1)
Text(DateManager().getMinuteWithTwoDigits(date: entry.date))
.font(Font(UIFont(name: "Copperplate-Bold", size: 86)!))
.multilineTextAlignment(.trailing)
.opacity(1)
.foregroundColor(.gray)
}
.padding(.trailing, 15)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Image(uiImage: #imageLiteral(resourceName: "moon")), alignment: .center)
}
}
It does show the UI elements:
https://github.com/beanut/images/blob/main/IMG_002DAA718D1C-1.jpeg?raw=true
Please help me with it and thanks in advance :)!
I stumbled upon this question trying to find a fix for this same issue for myself, and have since found a solution.
I don't believe the VStack was the problem here, but rather force unwrapping one or more of the values for the UI elements inside it, as in this line for example:
Text(DateManager().getDayOfWeekInString(date: entry.date)!)
Force unwrapping a nil value was what was stopping drawing widget's UI for all sizes for me. Try unwrapping the values the safe way or provide a fallback by nil coalescing.
This might be because you call WidgetCenter.shared.reloadAllTimelines in the getTimeline function:
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: #escaping (Timeline<Entry>) -> ()) {
...
for offset in 0 ..< 60 * 24 {
WidgetCenter.shared.reloadAllTimelines() // <- this is wrong
let entryDate = Calendar.current.date(byAdding: .minute, value: offset, to: midnight)!
entries.append(SimpleEntry(date: entryDate))
}
let timeline = Timeline(entries: entries, policy: .after(nextMidnight))
completion(timeline)
}
The whole point of WidgetCenter.shared.reloadAllTimelines is to force refresh the timeline and effectively call the getTimeline function again.
It's a bad idea to call it from inside getTimeline, especially in the loop.
The code in previews may be working because the getTimeline function is called once only and all repetitive calls are ignored.

RSS Widget For iOS 14 Shows Empty

I use an XMLParser for my app written in Swift, and am trying to get a Widget set up for iOS 14 in which it will show the most recent article on the RSS. I see the placeholder just fine when I add it, but it stays empty with just a line where text should be and never seems to get the information from the timeline.
struct Provider: TimelineProvider {
#State private var rssItems:[RSSItem]?
let feedParser = FeedParser()
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), title:"News", description: "News article here", link: "Http://link", pubDate: "The day it posted")
}
func getSnapshot(in context: Context, completion: #escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), title:"News", description: "News Article Here", link: "Http://link", pubDate: "The day it posted")
completion(entry)
}
func getTimeline(in context: Context, completion: #escaping (Timeline<SimpleEntry>) -> ()) {
var entries: [SimpleEntry] = []
feedParser.parseFeed(url: "http://fritchcoc.wordpress.com/feed") {(rssItems) in
self.rssItems = rssItems
let currentDate = Date()
let entry = SimpleEntry(date: currentDate, title:rssItems[0].title, description: rssItems[0].description, link: rssItems[0].link, pubDate: rssItems[0].pubDate)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let title: String
let description: String
let link: String
let pubDate: String
}
struct FritchNewsEntryView : View {
var entry: SimpleEntry
var body: some View {
VStack(alignment: .leading, spacing: 4) {
Text(entry.title)
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .leading)
.padding()
.background(LinearGradient(gradient: Gradient(colors: [.orange, .yellow]), startPoint: .top, endPoint: .bottom))
}
}
#main
struct FritchNews: Widget {
let kind: String = "FritchNews"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
FritchNewsEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
UPDATE BY THE OP:
It was indeed a combination of the timeline being set in the wrong spot, along with a couple of typos within the parser that caused this particular issue.
Apple does not allow HTTP connections by default, the easiest solution is to change the URL to https://fritchcoc.wordpress.com/feed
If you want to allow HTTP connections in your app (or widget) you can add exceptions (to one domain or all) in the Info.plist for the target, see NSAppTransportSecurity at Apple Docs
Edit:
After a better look and finding for the implementation of FeedParser online, I noticed that parseFeed() is asynchronous.
Thus the completion is called with an empty array, it should be called after the parsing is done:
struct Provider: TimelineProvider {
...
func getTimeline(in context: Context, completion: #escaping (Timeline<SimpleEntry>) -> ()) {
var entries: [SimpleEntry] = []
feedParser.parseFeed(url: "http://fritchcoc.wordpress.com/feed") {(rssItems) in
self.rssItems = rssItems
let currentDate = Date()
let entry = SimpleEntry(date: currentDate, title:rssItems[0].title, description: rssItems[0].description, link: rssItems[0].link, pubDate: rssItems[0].pubDate)
entries.append(entry)
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
}

Gradient overlay works on static Image, but not loaded Image from network

I'm trying to add a black gradient background over my img1 below:
struct Match: View {
let storage = Storages()
#ObservedObject var info = Info()
#State var name: Text = Text("")
#State var age: Text = Text("")
#State var bio: Text = Text("")
#State var img1: Image? = nil
#State var img2: Image? = nil
#State var img3: Image? = nil
var body: some View {
VStack {
ScrollView(.vertical, showsIndicators: true) {
ZStack(alignment: .bottomLeading){
LinearGradient(gradient: Gradient(colors: [Color.black.opacity(0.5), Color.black]), startPoint: .top, endPoint: .bottom)
img1?.resizable().scaledToFit()
name
.font(.system(size: 30))
.fontWeight(/*#START_MENU_TOKEN#*/.bold/*#END_MENU_TOKEN#*/)
.foregroundColor(.white)
.multilineTextAlignment(.leading)
.padding(EdgeInsets(top: 0, leading: 35, bottom: 55, trailing: 0))
age
.font(.system(size: 30))
.fontWeight(/*#START_MENU_TOKEN#*/.bold/*#END_MENU_TOKEN#*/)
.foregroundColor(.white)
.multilineTextAlignment(.leading)
.padding(EdgeInsets(top: 0, leading: 35, bottom: 20, trailing: 0))
}.background(Color.orange)
bio
.padding()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
.background(
Rectangle()
.foregroundColor(.white)
)
.foregroundColor(.black)
img2?.resizable().scaledToFit()
img3?.resizable().scaledToFit()
}.background(
RoundedRectangle(cornerRadius: 15)
.foregroundColor(Color("LightBlack70%"))
).clipShape(
RoundedRectangle(cornerRadius: 15)
).frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
.onAppear{
let match = Info().getMatch()
guard let uid = match["uid"] else {
return
}
self.bio = Text(match["bio"] as? String ?? "")
self.name = Text(match["name"] as? String ?? "")
self.age = Text(match["age"] as? String ?? "")
print("onAppear() matchImg1: \(match["img1"]!) ")
if let matchImg1 = match["img1"], let uid = match["uid"] {
self.storage.loadImageFromUrl(uid: uid as? String, imgName: matchImg1 as? String){ self.img1 = $0 }
}
if let matchImg2 = match["img2"]{
self.storage.loadImageFromUrl(uid: uid as? String, imgName: matchImg2 as? String){ self.img2 = $0 }
}
if let matchImg3 = match["img3"]{
self.storage.loadImageFromUrl(uid: uid as? String, imgName: matchImg3 as? String){ self.img3 = $0 }
}
}
}
}
However it is not showing. If I change img1 to a static image stored in my Assets.xcassets, the gradient works. But in the code above I am loading the image from a network and this appears to be affecting the gradient being rendered.
Any idea what the problem is?
Storages.swift
class Storages: ObservableObject {
let storage = Storage.storage()
let uid = UserAuth().uid ?? "<uid>"
#Published var img1 = UserDefaults.standard.string(forKey: "img1") ?? "<img1>"
#Published var photos: [String:String?] = [
"img1": UserDefaults.standard.string(forKey: "img1"),
"img2": UserDefaults.standard.string(forKey: "img2"),
"img3": UserDefaults.standard.string(forKey: "img3"),
]
func replaceImage(key: String, uiImage: UIImage){
guard let data = uiImage.jpegData(compressionQuality: 0.8) else {
return
}
// set upload path
let metaData = StorageMetadata()
metaData.contentType = "image/jpg"
let filename = "\(randomAlphanumeric()).jpg"
print("upload filename: \(filename)")
let ref = self.storage.reference().child("users/\(self.uid)/\(filename)")
// Upload the file to the path "images/rivers.jpg"
let uploadTask = ref.putData(data, metadata: metaData) { (metadata, error) in
print("upload task: \(metadata)")
print("error: \(error)")
guard let metadata = metadata else {
// Uh-oh, an error occurred!
return
}
UserDefaults.standard.set(filename, forKey: key)
}
}
func loadImageFromUrl(uid: String? = Storages().uid, imgName: String?, completion: #escaping (Image)->()) {
guard let imgName = imgName, let uid = uid else {
return
}
guard let url = URL(string: "http://app-4j34s.appspot.com.storage.googleapis.com/users/\(uid)/\(imgName)") else {
return
}
URLSession.shared.dataTask(with: url){ data, response, err in
if let data = data, let uiImage = UIImage(data: data) {
DispatchQueue.main.async {
completion(Image(uiImage: uiImage))
}
}
}.resume()
}
}
ContentView.swift
struct ContentView: View {
#ObservedObject var auth = UserAuth()
var body: some View {
Group {
if auth.uid != nil { Match() } else { LoginView(auth: auth) }
}
}
}

Resources