Skip to content

Commit 6ac43bf

Browse files
committedDec 4, 2024
Test
1 parent c1bb1c8 commit 6ac43bf

File tree

6 files changed

+172
-26
lines changed

6 files changed

+172
-26
lines changed
 

‎PG5602_H24-4/Entities/Search.swift

+14-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,17 @@
55
// Created by Karima Thingvold on 04/12/2024.
66
//
77

8-
import Foundation
8+
//import Foundation
9+
//import SwiftData
10+
//
11+
//@Model
12+
//class Search: Identifiable {
13+
// @Attribute(.unique) var query: String
14+
// var timestamp: Date
15+
//
16+
// init(query: String, timestamp: Date = Date()) {
17+
// self.query = query
18+
// self.timestamp = timestamp
19+
// }
20+
//}
21+

‎PG5602_H24-4/PG5602_H24_4App.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import SwiftData
1212
struct PG5602_H24_4App: App {
1313
var sharedModelContainer: ModelContainer = {
1414
do {
15-
let schema = Schema([Article.self])
15+
let schema = Schema([Article.self, Category.self, Country.self])
1616
return try ModelContainer(for: schema)
1717
} catch {
1818
fatalError("Could not create model container: \(error)")

‎PG5602_H24-4/Services/NewsAPIService.swift

+59-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ class NewsApiService {
1212
private let topHeadlinesURL = "https://newsapi.org/v2/top-headlines"
1313
private let everythingURL = "https://newsapi.org/v2/everything"
1414

15-
// Hent nyheter basert på en spesifikk type søk (top-headlines eller everything)
1615
func fetchNews(endpoint: String, query: String? = nil, completion: @escaping (Result<[NewsArticle], Error>) -> Void) {
1716
//#if DEBUG
1817
// Bruk mock-data for å unngå ekte API-kall i preview
@@ -78,6 +77,11 @@ class NewsApiService {
7877

7978
// Hent toppnyheter
8079
func fetchTopHeadlines(country: String? = nil, category: String? = nil, pageSize: Int = 20, completion: @escaping (Result<[NewsArticle], Error>) -> Void) {
80+
//#if DEBUG
81+
// completion(.success(MockData.articles))
82+
// return
83+
// #endif
84+
8185
let apiKey = APIConfig.apiKey
8286
guard !apiKey.isEmpty else {
8387
completion(.failure(NSError(domain: "", code: 401, userInfo: [NSLocalizedDescriptionKey: "API key is missing"])))
@@ -131,18 +135,67 @@ class NewsApiService {
131135
}.resume()
132136
}
133137

134-
135-
// Hent nyheter med et spesifikt søkeord
136-
func searchArticles(query: String, completion: @escaping (Result<[NewsArticle], Error>) -> Void) {
137-
fetchNews(endpoint: everythingURL, query: query, completion: completion)
138+
func searchArticles(query: String, sortBy: String, completion: @escaping (Result<[NewsArticle], Error>) -> Void) {
139+
//#if DEBUG
140+
// completion(.success(MockData.articles))
141+
// return
142+
// #endif
143+
144+
guard !query.isEmpty else {
145+
completion(.failure(NSError(domain: "", code: 400, userInfo: [NSLocalizedDescriptionKey: "Query is empty"])))
146+
return
147+
}
148+
149+
var queryItems = [
150+
URLQueryItem(name: "apiKey", value: APIConfig.apiKey),
151+
URLQueryItem(name: "q", value: query),
152+
URLQueryItem(name: "pageSize", value: "20")
153+
]
154+
155+
queryItems.append(URLQueryItem(name: "sortBy", value: sortBy))
156+
157+
guard var urlComponents = URLComponents(string: everythingURL) else {
158+
completion(.failure(NSError(domain: "", code: 400, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])))
159+
return
160+
}
161+
162+
urlComponents.queryItems = queryItems
163+
164+
guard let url = urlComponents.url else {
165+
completion(.failure(NSError(domain: "", code: 400, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])))
166+
return
167+
}
168+
169+
URLSession.shared.dataTask(with: url) { data, response, error in
170+
if let error = error {
171+
completion(.failure(error))
172+
return
173+
}
174+
175+
guard let data = data else {
176+
completion(.failure(NSError(domain: "", code: 500, userInfo: [NSLocalizedDescriptionKey: "No data received"])))
177+
return
178+
}
179+
180+
do {
181+
let decoder = JSONDecoder()
182+
decoder.dateDecodingStrategy = .iso8601
183+
let newsResponse = try decoder.decode(ArticlesResponse.self, from: data)
184+
completion(.success(newsResponse.articles))
185+
} catch {
186+
completion(.failure(error))
187+
}
188+
}.resume()
138189
}
190+
139191
}
140192

141193
struct MockData {
142194
static let articles = [
143195
NewsArticle(author: "Mock Author",title: "Mock Title 1", description: "This is a mock description 1", url: "https://example.com/1", urlToImage: "https://seeklogo.com/images/N/new-york-times-logo-EE0F194CA3-seeklogo.com.png", publishedAt: "2024-12-01T10:00:00Z"),
144-
196+
145197
NewsArticle(author: "Mock Author 2",title: "Mock Title 2", description: "This is a mock description 2", url: "https://example.com/2", urlToImage: "https://seeklogo.com/images/B/bbc-news-logo-8648ABD044-seeklogo.com.png", publishedAt: "2024-12-01T10:00:00Z"),
198+
146199
NewsArticle(author: "Mock Author 3",title: "Mock Title 3", description: "This is a mock description 2", url: "https://example.com/3", urlToImage: "https://download.logo.wine/logo/CNN/CNN-Logo.wine.png", publishedAt: "2024-12-01T10:00:00Z"),
147200
]
148201
}

‎PG5602_H24-4/Views/ArticleDetailedView.swift

+13-13
Original file line numberDiff line numberDiff line change
@@ -187,16 +187,16 @@ struct ArticleDetailView: View {
187187
}
188188
}
189189

190-
#Preview {
191-
let exampleArticle = NewsArticle(
192-
author: "John Doe",
193-
title: "Breaking News: Swift is Awesome!",
194-
description: "SwiftUI helps you build great-looking apps across all Apple platforms with the power of Swift — and surprisingly little code. You can bring even better experiences to everyone, on any Apple device, using just one set of tools and APIs.",
195-
url: "https://developer.apple.com/xcode/swiftui/",
196-
urlToImage: "https://developer.apple.com/xcode/swiftui/images/hero-lockup-swiftui-large_2x.webp",
197-
publishedAt: "2024-11-25T12:10:00Z"
198-
)
199-
200-
ArticleDetailView(article: exampleArticle)
201-
.modelContainer(for: [Article.self]) // Mock ModelContext for SwiftData
202-
}
190+
//#Preview {
191+
// let exampleArticle = NewsArticle(
192+
// author: "John Doe",
193+
// title: "Breaking News: Swift is Awesome!",
194+
// description: "SwiftUI helps you build great-looking apps across all Apple platforms with the power of Swift — and surprisingly little code. You can bring even better experiences to everyone, on any Apple device, using just one set of tools and APIs.",
195+
// url: "https://developer.apple.com/xcode/swiftui/",
196+
// urlToImage: "https://developer.apple.com/xcode/swiftui/images/hero-lockup-swiftui-large_2x.webp",
197+
// publishedAt: "2024-11-25T12:10:00Z"
198+
// )
199+
//
200+
// ArticleDetailView(article: exampleArticle)
201+
// .modelContainer(for: [Article.self]) // Mock ModelContext for SwiftData
202+
//}

‎PG5602_H24-4/Views/MainLayoutView.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ struct MainLayoutView: View {
1010
var body: some View {
1111
TabView {
1212
Tab("My Artichles", systemImage: "doc.text") {
13-
NewsTickerView()
13+
//NewsTickerView()
1414
ArticlesView()
1515
}
1616
Tab("Search", systemImage: "magnifyingglass") {

‎PG5602_H24-4/Views/SearchView.swift

+84-4
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,79 @@
66
//
77

88
import SwiftUI
9+
import SwiftData
10+
11+
enum SortOption: String, CaseIterable {
12+
case relevance = "Relevance"
13+
case popularity = "Popularity"
14+
case publishedAt = "Newest"
15+
}
916

1017
struct SearchView: View {
18+
@Environment(\.modelContext) private var modelContext
19+
//@Query(sort: \Search.timestamp, order: .reverse) var searchHistory: [Search]
20+
//@State var searchHistory: [Search] = []
21+
1122
@State var query: String = ""
1223
@State var articles: [NewsArticle] = []
13-
@State var isLoading: Bool = false
24+
@State var isLoading = false
1425
@State var errorMessage: String?
26+
@State var selectedSortOption: SortOption = .relevance
27+
@State var selectedQuery = ""
1528

1629
var body: some View {
1730
NavigationStack {
1831
VStack {
1932
HStack {
2033
Image(systemName: "magnifyingglass")
21-
.foregroundColor(.gray)
34+
.foregroundStyle(.gray)
2235
TextField("Search for articles", text: $query, onCommit: {
2336
search()
2437
})
2538
.textFieldStyle(PlainTextFieldStyle())
39+
// Menu {
40+
// ForEach(searchHistory) { search in
41+
// Button(action: {
42+
// query = search.query // Sett søkefeltet til valgt søkeord
43+
// performSearch() // Utfør søket
44+
// }) {
45+
// Text(search.query)
46+
// }
47+
// }
48+
// } label: {
49+
// Image(systemName: "arrowtriangle.down.fill")
50+
// .foregroundColor(.blue)
51+
// .padding(.horizontal, 8)
52+
// }
2653
}
2754
.padding(10)
2855
.background(Color(.systemGray6))
2956
.cornerRadius(8)
3057
.padding()
3158

59+
HStack {
60+
ForEach(SortOption.allCases, id: \.self) { option in
61+
Button(action: {
62+
selectedSortOption = option
63+
sortArticles()
64+
}) {
65+
Text(option.rawValue)
66+
.padding()
67+
.background(selectedSortOption == option ? Color.blue : Color.gray.opacity(0.3))
68+
.foregroundStyle(.white)
69+
.clipShape(RoundedRectangle(cornerRadius: 8))
70+
//.padding()
71+
72+
}
73+
}
74+
}
75+
3276
if isLoading {
3377
ProgressView("Loading articles")
3478
.padding()
3579
} else if let errorMessage = errorMessage {
3680
Text(errorMessage)
37-
.foregroundColor(.red)
81+
.foregroundStyle(.red)
3882
.padding()
3983
} else {
4084
List(articles, id: \.url) { article in
@@ -63,7 +107,7 @@ struct SearchView: View {
63107

64108
let newsService = NewsApiService()
65109

66-
newsService.searchArticles(query: query) { result in
110+
newsService.searchArticles(query: query, sortBy: selectedSortOption.rawValue.lowercased()) { result in
67111
DispatchQueue.main.async {
68112
isLoading = false
69113
switch result {
@@ -75,6 +119,42 @@ struct SearchView: View {
75119
}
76120
}
77121
}
122+
123+
func sortArticles() {
124+
switch selectedSortOption {
125+
case .relevance:
126+
articles.sort { $0.title > $1.title }
127+
case .popularity:
128+
articles.sort { $0.author ?? "" > $1.author ?? "" }
129+
case .publishedAt:
130+
articles.sort { $0.publishedAt > $1.publishedAt }
131+
}
132+
}
133+
func performSearch() {
134+
guard !query.isEmpty else { return }
135+
isLoading = true
136+
errorMessage = nil
137+
//saveSearch(query: query)
138+
139+
let newsService = NewsApiService()
140+
newsService.searchArticles(query: query, sortBy: "relevance") { result in
141+
DispatchQueue.main.async {
142+
isLoading = false
143+
switch result {
144+
case .success(let articles):
145+
self.articles = articles
146+
case .failure(let error):
147+
self.errorMessage = "Something went wrong: \(error.localizedDescription)"
148+
}
149+
}
150+
}
151+
}
152+
// func saveSearch(query: String) {
153+
// if !searchHistory.contains(where: { $0.query == query }) {
154+
// let newSearch = Search(query: query)
155+
// modelContext.insert(newSearch)
156+
// }
157+
// }
78158
}
79159

80160
#Preview {

0 commit comments

Comments
 (0)
Please sign in to comment.