Skip to content

Commit da1d8c4

Browse files
committed
Add search for apple documentations
1 parent 0ac4111 commit da1d8c4

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import Foundation
2+
import SwiftSoup
3+
import WebKit
4+
import WebScrapper
5+
6+
struct AppleDocumentationSearchService: SearchService {
7+
func search(query: String) async throws -> WebSearchResult {
8+
let queryEncoded = query
9+
.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""
10+
let url = URL(string: "https://developer.apple.com/search/?q=\(queryEncoded)")!
11+
12+
let scrapper = await WebScrapper()
13+
let html = try await scrapper.fetch(url: url) { document in
14+
DeveloperDotAppleResultParser.validate(document: document)
15+
}
16+
17+
return try DeveloperDotAppleResultParser.parse(html: html)
18+
}
19+
}
20+
21+
enum DeveloperDotAppleResultParser {
22+
static func validate(document: SwiftSoup.Document) -> Bool {
23+
guard let _ = try? document.select("ul.search-results").first
24+
else { return false }
25+
return true
26+
}
27+
28+
static func parse(html: String) throws -> WebSearchResult {
29+
let document = try SwiftSoup.parse(html)
30+
let searchResult = try? document.select("ul.search-results").first
31+
32+
guard let searchResult else { return .init(webPages: []) }
33+
34+
var results: [WebSearchResult.WebPage] = []
35+
for element in searchResult.children() {
36+
if let titleElement = try? element.select("p.result-title"),
37+
let link = try? titleElement.select("a").attr("href"),
38+
!link.isEmpty
39+
{
40+
let title = (try? titleElement.text()) ?? ""
41+
let snippet = (try? element.select("p.result-description").text())
42+
?? (try? element.select("ul.breadcrumb-list").text())
43+
?? ""
44+
results.append(WebSearchResult.WebPage(
45+
urlString: {
46+
if link.hasPrefix("/") {
47+
return "https://developer.apple.com\(link)"
48+
}
49+
return link
50+
}(),
51+
title: title,
52+
snippet: snippet
53+
))
54+
}
55+
}
56+
57+
return WebSearchResult(webPages: results)
58+
}
59+
}
60+

Tool/Sources/WebSearchService/WebSearchService.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public enum WebSearchProvider {
1919

2020
case serpAPI(SerpAPIEngine, apiKey: String)
2121
case headlessBrowser(HeadlessBrowserEngine)
22+
case appleDocumentation
2223

2324
public static var userPreferred: WebSearchProvider {
2425
switch UserDefaults.shared.value(for: \.searchProvider) {
@@ -63,6 +64,8 @@ public struct WebSearchService {
6364
service = SerpAPISearchService(engine: engine, apiKey: apiKey)
6465
case let .headlessBrowser(engine):
6566
service = HeadlessBrowserSearchService(engine: engine)
67+
case .appleDocumentation:
68+
service = AppleDocumentationSearchService()
6669
}
6770
}
6871

0 commit comments

Comments
 (0)