News Widget by SwiftUI
Step_01: Get the news api by login this website: https://newsapi.org
Then you will get same this api “09510d0f4f3641258f6f703aa2ef9611
“
After get the api key, now hit the api in Postman and you will see the JSON response data same as below:
{
"status": "ok",
"totalResults": 24,
"articles": [
{
"source": {
"id": "the-wall-street-journal",
"name": "The Wall Street Journal"
},
"author": "Jean Eaglesham",
"title": "Climate Promises by Businesses Face New Scrutiny",
"description": "Emissions pledges from U.N. Glasgow conference will be closely watched",
"url": "https://www.wsj.com/articles/climate-promises-by-businesses-face-new-scrutiny-11636104600?mod=hp_lead_pos2",
"urlToImage": "https://images.wsj.net/im-429222/social",
"publishedAt": "2021-11-05T09:30:00Z",
"content": "The United Nations conference on climate change in Glasgow has been full of promises by companies to reduce their carbon emissions. How many will live up to them and how will anyone know if they are … [+333 chars]"
},
{
"source": {
"id": "the-wall-street-journal",
"name": "The Wall Street Journal"
},
"author": "Jean Eaglesham",
"title": "Climate Promises by Businesses Face New Scrutiny",
"description": "Emissions pledges from U.N. Glasgow conference will be closely watched",
"url": "https://www.wsj.com/articles/climate-promises-by-businesses-face-new-scrutiny-11636104600?mod=hp_lead_pos2",
"urlToImage": "https://images.wsj.net/im-429222/social",
"publishedAt": "2021-11-05T09:30:00Z",
"content": "The United Nations conference on climate change in Glasgow has been full of promises by companies to reduce their carbon emissions. How many will live up to them and how will anyone know if they are … [+333 chars]"
}
]
}
Step_02: Now create new project selecting iOS target choosen project name:
Step_03: Create new swift file name NewsArticleList & NewsArticle and also added below code:
struct NewsArticleList: Codable {
var status: String
var totalResults: Int
var articles: [NewsArticle]
}
struct NewsArticle: Codable {
var author: String?
var title: String
var description: String?
var url: String?
var urlToImage: String?
var content: String?
var publishedAt: String?
}
extension NewsArticle: Identifiable {
var id: String {
return title
}
}
Step_04: Create APIService class for In order to retrieve the data from News API, we need a simple API service which will retrieve the JSON and convert it to a Codable Dataset. So create the following:
final class APIService {
enum APIError: Error {
case unknownError
}
func getObject<T: Codable>(object: T.Type, url: URL) -> AnyPublisher<T, Error> {
return URLSession.shared.dataTaskPublisher(for: url)
.tryMap { (data, response) -> Data in
guard let httpResponse = response as? HTTPURLResponse else {
throw APIError.unknownError
}
guard 200...299 ~= httpResponse.statusCode else {
throw URLError(URLError.Code(rawValue: httpResponse.statusCode), userInfo: httpResponse.allHeaderFields as? [String: Any] ?? [:] )
}
return data
}
.decode(type: T.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
}
Step_05: For that, we create a view model for retrieving the data from News API with an observable state, which will contain the data.
import Foundation
import Combine
final class NewsViewModel: ObservableObject {
// MARK: - Constants
private enum Constants {
static let endpointString = "https://newsapi.org/v2/top-headlines?country=us&sortBy=publishedAt"
static let apiKey = "09510d0f4f3641258f6f703aa2ef9612"
}
enum Category: String {
case general
case business
case entertainment
case health
case science
case sports
case technology
}
// MARK: - state
enum ResultState {
case loading
case error(Error)
case success([NewsArticle])
}
// MARK: - properties
private var cancelables: Set<AnyCancellable> = Set<AnyCancellable>()
private let apiService: APIService
@Published var state: ResultState = .loading
init(apiService: APIService = APIService() ) {
self.apiService = apiService
}
func getDataIfNeeded(category: Category = .general) {
guard let url = getUrlForCategory(category: category) else {
//completion?(.failure(APIService.APIError.unknownError))
return
}
state = .loading
apiService.getObject(object: NewsArticleList.self, url: url)
.subscribe(on: DispatchQueue.global(qos: .background))
.receive(on: DispatchQueue.main)
.sink { [weak self] result in
switch result {
case .failure(let error):
self?.state = .error(error)
//completion?(.failure(error))
default: break
}
} receiveValue: { [weak self] list in
self?.state = .success(list.articles)
}.store(in: &cancelables)
}
private func getUrlForCategory(category: Category = .general) -> URL? {
var urlComponents = URLComponents(string: Constants.endpointString)
var queryItems = urlComponents?.queryItems
queryItems?.append(URLQueryItem(name: "category", value: category.rawValue))
queryItems?.append(URLQueryItem(name: "apiKey", value: Constants.apiKey))
urlComponents?.queryItems = queryItems
return urlComponents?.url
}
}
Step_06: Now added below code in you ContentView for UI:
import SwiftUI
import Combine
struct ContentView: View {
@ObservedObject var newsViewModel = NewsViewModel()
var body: some View {
GeometryReader { geometry in
switch newsViewModel.state {
case .loading:
Text("Loading...")
.frame(width: geometry.size.width, height: geometry.size.height, alignment: .center)
case .success(let result):
List(result) { item in
Text(item.title)
}
case .error:
Text("Error occured!")
.frame(width: geometry.size.width, height: geometry.size.height, alignment: .center)
}
}.onAppear {
newsViewModel.getDataIfNeeded()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Now if you run your project you will see news list of data. That was our general iPhone OS part. Now we will make widget & show data in widget fetching from NewsAPI data.
Widget_Step_01: Now we will create Widget Extension. Select project file then click File->New->Target. select iOS item and then search for widget extension.
For get the news value, we need to set target NewsWidgetExtension for all of classes:
1. NewsArticle
2. NewsArticleList
3. APIServices
4. NewsViewModel
Widget_Step_02: Modify NewsViewModel class like below:
import Foundation
import Combine
final class NewsViewModel: ObservableObject {
// MARK: - Constants
private enum Constants {
static let endpointString = "https://newsapi.org/v2/top-headlines?country=us&sortBy=publishedAt"
static let apiKey = "09510d0f4f3641258f6f703aa2ef9612"
}
// MARK: - properties
enum Category: String {
case general
case business
case entertainment
case health
case science
case sports
case technology
}
enum ResultState {
case loading
case error(Error)
case success([NewsArticle])
}
private var cancelables: Set<AnyCancellable> = Set<AnyCancellable>()
private let apiService: APIService
@Published var state: ResultState = .loading
// MARK: - init
init(apiService: APIService = APIService() ) {
self.apiService = apiService
}
// MARK: - get data
func getDataIfNeeded(category: Category = .general, completion: ((Result<[NewsArticle], Error>) -> Void )? = nil ) {
guard let url = getUrlForCategory(category: category) else {
completion?(.failure(APIService.APIError.unknownError))
return
}
state = .loading
apiService.getObject(object: NewsArticleList.self, url: url)
.subscribe(on: DispatchQueue.global(qos: .background))
.receive(on: DispatchQueue.main)
.sink { [weak self] result in
switch result {
case .failure(let error):
self?.state = .error(error)
completion?(.failure(error))
default: break
}
} receiveValue: { [weak self] list in
self?.state = .success(list.articles)
completion?(.success(list.articles))
}.store(in: &cancelables)
}
private func getUrlForCategory(category: Category = .general) -> URL? {
var urlComponents = URLComponents(string: Constants.endpointString)
var queryItems = urlComponents?.queryItems
queryItems?.append(URLQueryItem(name: "category", value: category.rawValue))
queryItems?.append(URLQueryItem(name: "apiKey", value: Constants.apiKey))
urlComponents?.queryItems = queryItems
return urlComponents?.url
}
}
Widget_Step_03: In NewsWidget struck Refactor SimpleEntry by NewsListEntry. And added below code. If you see any error configuration then fixed it by state case set .idle:
struct NewsListEntry: TimelineEntry {
enum State{
case idle
case error
case success([NewsArticle])
}
let date: Date
let state: State
}
Widget_Step_04: After successfully build the app, now create NewsViewModel object in Provider struct. Call the api for get data in timeline function and added code same as below:
struct Provider: AppIntentTimelineProvider {
private let newsViewModel: NewsViewModel = NewsViewModel()
func placeholder(in context: Context) -> NewsListEntry {
NewsListEntry(date: Date(), state: .idle)
}
func snapshot(for configuration: ConfigurationAppIntent, in context: Context) async -> NewsListEntry {
NewsListEntry(date: Date(), state: .idle)
}
func timeline(for configuration: ConfigurationAppIntent, in context: Context) async -> Timeline<NewsListEntry> {
var entries: [NewsListEntry] = []
newsViewModel.getDataIfNeeded{ result in
switch result {
case .success(let newsItems):
let items = Array(newsItems.prefix(4))
entries.append(NewsListEntry(date: Date(), state: .success(items)))
case .failure:
entries.append(NewsListEntry(date: Date(), state: .error))
break
}
}
return Timeline(entries: entries, policy: .atEnd)
}
}
Widget_Step_05: Now set your NewsWidgetEntryView below like:
struct NewsWidgetEntryView : View {
var entry: NewsListEntry
var body: some View {
GeometryReader { geometry in
switch entry.state {
case .idle:
Text("No data yet, waiting")
.font(.system(size: 12))
.frame(width: geometry.size.width, height: geometry.size.height, alignment: .center)
case .error:
Text("Oops something went wrong - please reconfigure")
.font(.system(size: 12))
.frame(width: geometry.size.width, height: geometry.size.height, alignment: .center)
case .success(let array):
VStack {
Text("My News Widget")
Spacer()
ForEach(array) { array in
Text(array.title)
.frame(maxWidth: geometry.size.width, alignment: .leading)
.font(.system(size: 12))
.multilineTextAlignment(.leading)
}
Spacer()
}
}
}
}
}
I never thought about it that way before, what an interesting perspective.
Wow, fantastic weblog structure! How long have you ever been running
a blog for? you made blogging look easy. The whole glance of
your site is wonderful, as smartly as the content! You can see similar here sklep
Несомненно стильные новости модного мира.
Актуальные события мировых подуимов.
Модные дома, торговые марки, высокая мода.
Новое место для модных людей.
https://fashionvipclub.ru/news/2024-06-19-gruzin-kotoryy-perevernul-mirovuyu-modu-demna-gvasaliya/
Полностью трендовые события подиума.
Абсолютно все эвенты мировых подуимов.
Модные дома, лейблы, высокая мода.
Новое место для трендовых людей.
https://hypebeasts.ru/
Полностью стильные новости мировых подиумов.
Актуальные эвенты лучших подуимов.
Модные дома, бренды, высокая мода.
Лучшее место для стильныех хайпбистов.
https://luxe-moda.ru/chic/162-loro-piana-lyubimyy-brend-politikov-i-biznesmenov/
Несомненно свежие новости подиума.
Важные события самых влиятельных подуимов.
Модные дома, лейблы, гедонизм.
Новое место для модных людей.
https://balmain1.ru/balmain/381-kak-otlichit-originalnyy-balmain-ot-poddelki/
Самые стильные новости моды.
Исчерпывающие новости самых влиятельных подуимов.
Модные дома, бренды, haute couture.
Лучшее место для трендовых хайпбистов.
https://km-moda.ru/style/525-parajumpers-istoriya-stil-i-assortiment/
Самые свежие события мировых подиумов.
Абсолютно все новости мировых подуимов.
Модные дома, лейблы, гедонизм.
Самое лучшее место для трендовых хайпбистов.
https://luxe-moda.ru/chic/356-rick-owens-buntar-v-chernyh-tonah/
Полностью актуальные события модного мира.
Актуальные новости известнейших подуимов.
Модные дома, торговые марки, гедонизм.
Лучшее место для модных хайпбистов.
https://modastars.ru/
Полностью важные новости подиума.
Важные новости лучших подуимов.
Модные дома, лейблы, высокая мода.
Приятное место для модных людей.
https://donnafashion.ru/
Наиболее свежие события индустрии.
Исчерпывающие события лучших подуимов.
Модные дома, лейблы, высокая мода.
Новое место для трендовых людей.
https://donnafashion.ru/
Наиболее актуальные события индустрии.
Все новости мировых подуимов.
Модные дома, бренды, haute couture.
Интересное место для модных людей.
https://mvmedia.ru/novosti/282-vybiraem-puhovik-herno-podrobnyy-gayd/
Абсолютно стильные новинки индустрии.
Важные новости мировых подуимов.
Модные дома, торговые марки, высокая мода.
Интересное место для стильныех людей.
https://lecoupon.ru/
Точно стильные новости подиума.
Важные мероприятия самых влиятельных подуимов.
Модные дома, торговые марки, гедонизм.
Лучшее место для модных хайпбистов.
https://chelyabinsk.rftimes.ru/news/2024-04-20-obuchenie-molodyh-spetsialistov-v-ramkah-programmy-masterskaya-novyh-media
Очень стильные новинки моды.
Исчерпывающие эвенты мировых подуимов.
Модные дома, бренды, гедонизм.
Самое приятное место для модных хайпбистов.
https://krasnodar.rftimes.ru/news/2024-05-31-permskiy-zakladchik-zastrelil-politseyskogo-v-lesu-pod-krasnodarom
Модные советы по подбору отличных луков на каждый день.
Мнения экспертов, события, все новые коллекции и шоу.
https://rftimes.ru/news/2024-08-14-7-samyh-kultovyh-veshchey-ot-balenciaga
Бренд Balenciaga является выдающимся домов высокой моды, который создался в начале 20 века легендарным кутюрье Кристобалем Баленсиагой. Его узнают уникальными моделями и необычными силуэтами, противоречат стандартам индустрии.
https://balenciaga.whitesneaker.ru/
Вещи бренда Fendi представлены у нас. Широкий ассортимент Fendi поможет подобрать идеальные вещи для любого случая.
https://fendi.sneakerside.ru
Bottega Veneta — это престижный итальянский бренд, известный неповторимым стилем. Основанный в 1966 году, бренд стал символом стиля и элегантности и славится классическими аксессуарами. Дизайны бренда отражают искусность и внимательность к деталям, а также уникальный подход к дизайну.
https://bottega-official.ru
Официальный интернет-магазин Боттега Венета предлагает разнообразие изделий премиум-класса от итальянского бренда. В нашем каталоге вы сможете выбрать и заказать изделия последних поступлений с возможностью доставки по Москве и всей России.
https://bottega-official.ru
Ti le keo malaisia là một trong những thông tin quan trọng nhất đối với người chơi cá cược.
buy priligy in the usa Bradbury ML, Mullin CM, Gillian SD, Weisse C, Bergman PJ, Morges MA, May LR, Vail DM, Clifford CA
Ищите в гугле
На этом сайте вы можете найти полезной информацией о терапии депрессии у людей старшего возраста. Здесь представлены советы и обзоры методов борьбы с этим заболеванием.
http://www.autismwesterncape.org.za/2015/11/11/blockquote-post/
На данном сайте вы найдёте полезную информацию о терапии депрессии у пожилых людей. Вы также узнаете здесь о профилактических мерах, актуальных подходах и рекомендациях специалистов.
http://forum.zplatformu.com/index.php?topic=296550.new
clomiphene generic 4 solution for 20 seconds