Skip to content
This repository has been archived by the owner on Aug 27, 2023. It is now read-only.

Commit

Permalink
Merge pull request #13 from Raureif/develop
Browse files Browse the repository at this point in the history
3.0
  • Loading branch information
frankrausch authored Nov 13, 2020
2 parents 453124d + b8b4a5c commit 5253906
Show file tree
Hide file tree
Showing 20 changed files with 366 additions and 569 deletions.
110 changes: 41 additions & 69 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
## WikipediaKit · API Client Framework for Swift

The [Wikipedia API](https://www.mediawiki.org/wiki/Special:ApiSandbox) is a complex beast. It takes some time (and willpower) to learn all of its idiosyncrasies.
The [Wikipedia API](https://www.mediawiki.org/wiki/Special:ApiSandbox) can do a lot, but it’s not easy to get started.

With WikipediaKit, it’s easy to build apps that search and show Wikipedia content—without worrying about the raw API. Instead of exposing all options and endpoints, WikipediaKit provides comfortable access to the most interesting parts. It’s opinionated—but that’s the point!
With WikipediaKit, it’s easy to build apps that search and show Wikipedia content—without worrying about the raw API. Instead of exposing all options and endpoints, WikipediaKit provides comfortable access to the most interesting parts for building a reader app. WikipediaKit comes with opinions and an attitude—but that’s the point!

The WikipediaKit framework is written in Swift, has no third-party dependencies, and runs on macOS, iOS, watchOS, and tvOS.

If this doesn’t convince you to build a shiny new reader app for Wikipedia yourself, have a look at [V for Wiki](http://v-for-wiki.com), the award-winning app that WikipediaKit was created for.
If this doesn’t convince you to build a shiny new reader app for Wikipedia yourself, have a look at [V for Wiki](http://v-for-wiki.com), the award-winning app that WikipediaKit was created for. The shipping version on the iOS App Store uses exactly the code that you can explore and download here.

## Installation

### Swift Package Manager (preferred)
WikipediaKit can be added to your Xcode project using the [Swift Package Manager](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app).

### Carthage
You can use [Carthage](https://github.com/Carthage/Carthage) to install and update WikipediaKit.

### Manual
Drag and drop the `WikipediaKit.xcodeproj` to your app project and add the `WikipediaKit` embedded framework in your app project’s build settings.

### Swift Package Manager

WikipediaKit is ready for the [Swift Package Manager](https://swift.org/package-manager/).


## Usage

Expand All @@ -45,7 +44,7 @@ WikipediaNetworking.appAuthorEmailForAPI = "[email protected]"

WikipediaKit will use this email address and your app’s bundle info to generate and send a `User-Agent` header. This will identify your app to Wikipedia’s servers with every API request, as required by the [API guidelines](https://www.mediawiki.org/wiki/API:Main_page#Identifying_your_client).

The `User-Agent` header is printed to your Xcode console when you make the first API request. It’ll look something like this:
The `User-Agent` header is printed to your Xcode console when you make the first API request. It’ll look similar to this:

```
User-Agent: ExampleApp/1.0 (com.example.ExampleApp; [email protected]) WikipediaKit/1.0
Expand Down Expand Up @@ -82,13 +81,13 @@ A `WikipediaLanguage` has a language code, a localized name, an [autonym](https:

```swift
// French language, localized name for German, no variant
let l = WikipediaLanguage(code: "fr",
let l = WikipediaLanguage(code: "fr",
localizedName: "Französisch", // FR in DE
autonym: "Français") // FR in FR
```


WikipediaKit comes with a list of most Wikipedia languages and their autonyms. This lets you initialize a `WikipediaLanguage` by passing only the language code. Please note that if you use this shorthand method, the localized names will be in English.
WikipediaKit comes with a list of Wikipedia languages and their autonyms. This lets you initialize a `WikipediaLanguage` by passing the language code. Please note that if you use this shorthand method, the localized names will be in English.

```swift
let language = WikipediaLanguage("fr")
Expand All @@ -106,7 +105,7 @@ let _ = Wikipedia.shared.requestOptimizedSearchResults(language: language, term:

guard error == nil else { return }
guard let searchResults = searchResults else { return }

for articlePreview in searchResults.items {
print(articlePreview.displayTitle)
}
Expand Down Expand Up @@ -148,22 +147,21 @@ The `displayTitle` and `displayText` can be formatted via your `WikipediaFormatt

### Articles

*Update: Since WikipediaKit 3.0, this method uses the new Wikipedia REST API. The rewrite was a good opportunity to modernize WikipediaKit and return a `Result<WikipediaArticle, WikipediaError>` type.*

Load the article about “[Soft rime](https://en.wikipedia.org/wiki/Soft_rime)” in English like this:

```swift
let language = WikipediaLanguage("en")

// You need to pass in the maximum width
// in pixels for the returned thumbnail URL,
// for example the screen width:
let imageWidth = Int(self.view.frame.size.width * UIScreen.main.scale)

let _ = Wikipedia.shared.requestArticle(language: language, title: "Soft rime", imageWidth: imageWidth) { (article, error) in
guard error == nil else { return }
guard let article = article else { return }

print(article.displayTitle)
print(article.displayText)
let _ = Wikipedia.shared.requestArticle(language: language, title: "Soft rime") { result in
switch result {
case .success(let article):
print(article.displayTitle)
print(article.displayText)
case .failure(let error):
print(error)
}
}
```

Expand All @@ -184,10 +182,10 @@ This search mode returns geo-tagged articles around a specific location. Pass in
let language = WikipediaLanguage("en")

let _ = Wikipedia.shared.requestNearbyResults(language: language, latitude: 52.4555592, longitude: 13.3175333) { (articlePreviews, resultsLanguage, error) in

guard error == nil else { return }
guard let articlePreviews = articlePreviews else { return }

for a in articlePreviews {
print(a.displayTitle)
if let coordinate = a.coordinate {
Expand All @@ -199,30 +197,29 @@ let _ = Wikipedia.shared.requestNearbyResults(language: language, latitude: 52.4
```


### Most Read Articles
### Featured Articles

The `requestFeaturedArticles(language:date:)` query gets a list of the most popular articles for a specific date from Wikipedia’s official analytics.

The `requestMostReadArticles(language:date:)` query gets a list of the most popular articles for a specific date from Wikipedia’s official analytics.
*Please note: Versions of WikipediaKit before 3.0 used the raw data from an older Wikipedia API to implement this feature. The new (current) implementation uses the same new API as the official Wikipedia app, which seems to filter the articles, stripping out potentially offensive content.*

```swift
let language = WikipediaLanguage("en")

let dayBeforeYesterday = Date(timeIntervalSinceNow: -60 * 60 * 48)

let _ = Wikipedia.shared.requestMostReadArticles(language: language, date: dayBeforeYesterday) { (articlePreviews, date, resultsLanguage, error) in

guard error == nil else { return }
guard let articlePreviews = articlePreviews else { return }

for a in articlePreviews {
print(a.displayTitle)
let _ = Wikipedia.shared.requestFeaturedArticles(language: language, date: dayBeforeYesterday) { result in
switch result {
case .success(let featuredCollection):
for a in featuredCollection.mostReadArticles {
print(a.displayTitle)
}
case .failure(let error):
print(error)
}
}
```

**Please note:** The API will return a `404` error if there’s no data available for the requested date. The most recent data is usually from yesterday, so receiving a `404` for today is expected.

You may want to filter the results from the most read articles before displaying them in your app—they are potentially NSFW. That’s what the `WikipediaBlacklistDelegate` is for (see below).

### Image Metadata

To find out the URL for a given Wikipedia image at a specific size, use this call:
Expand All @@ -231,7 +228,7 @@ To find out the URL for a given Wikipedia image at a specific size, use this cal
let language = WikipediaLanguage("en")

// You can pass multiple images here.
// Make sure to limit the number somehow
// Make sure to limit the number somehow
// because the API server will bail out
// if the query URL gets too long.

Expand All @@ -243,7 +240,7 @@ let _ = Wikipedia.shared.(language: language, urls: urls, width: 1000) { (images
print(metadata.url) // URL for 1000px width version
print(metadata.description)
print(metadata.license)
}
}
}

```
Expand Down Expand Up @@ -274,7 +271,7 @@ You can parse and reformat article texts, titles, and the table of contents in y

```swift
class MyFormattingDelegate: WikipediaTextFormattingDelegate {

static let shared = MyFormattingDelegate()

func format(context: WikipediaTextFormattingDelegateContext, rawText: String, title: String?, language: WikipediaLanguage, isHTML: Bool) -> String {
Expand All @@ -285,31 +282,6 @@ class MyFormattingDelegate: WikipediaTextFormattingDelegate {
```


### Blacklist Delegate

```swift
Wikipedia.sharedBlacklistDelegate = MyBlacklistDelegate.shared
```

You may wish to filter the most read articles before showing them as recommendations in your app. The `WikipediaBlacklistDelegate` protocol allows you to blacklist articles by title or description.

```swift
class MyBlacklistDelegate: WikipediaBlacklistDelegate {

func isBlacklistedForRecommendations(title: String,
language: WikipediaLanguage) -> Bool {
// Decide if title is blacklisted here…
return false
}

func containsBlacklistedWords(text: String, language: WikipediaLanguage) -> Bool {
// Decide if text contains blacklisted words…
return false
}

}
```

## Caching

Caching happens automatically (*after* processing and formatting) for search results and articles. WikipediaKit uses simple `NSCache` instances.
Expand All @@ -324,7 +296,7 @@ Request an array of random `WikipediaArticlePreview` objects like this:
```swift
Wikipedia.shared.requestRandomArticles(language: self.language, maxCount: 8, imageWidth: 640) {
(articlePreviews, language, error) in

guard let articlePreviews = articlePreviews else { return }

for article in articlePreviews {
Expand All @@ -333,12 +305,12 @@ Wikipedia.shared.requestRandomArticles(language: self.language, maxCount: 8, ima
}
```

WikipediaKit also has this convenience function that gets only one single random `WikipediaArticlePreview` at a time:
WikipediaKit has this convenience function that gets one single random `WikipediaArticlePreview` at a time:

```swift
Wikipedia.shared.requestSingleRandomArticle(language: self.language, maxCount: 8, imageWidth: 640) {
(article, language, error) in

guard let article = article else { return }

print(article.displayTitle)
Expand All @@ -357,7 +329,7 @@ If `maxCount` is larger than `1`, the surplus results from the API query are buf

WikipediaKit was created by [Frank Rausch](http://frankrausch.com) ([@frankrausch](https://twitter.com/frankrausch)) for [Raureif](http://raureif.net).

© 2017 Raureif GmbH / Frank Rausch
© 2017–20 Raureif GmbH / Frank Rausch

### License

Expand Down
3 changes: 1 addition & 2 deletions Sources/String+Wikipedia.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ extension String {

var characterSet = NSMutableCharacterSet.urlQueryAllowed

// Comma must not be encoded, otherwise the Most Read articles API call will not work on ru.wikipedia.org
var delimitersToEncode = ":#[]@!$?&'()*+="
var delimitersToEncode = "#[]@!$?&'()*+="

if encodeSlashes {
delimitersToEncode.append("/")
Expand Down
74 changes: 24 additions & 50 deletions Sources/Wikipedia+Article.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// Wikipedia+Article.swift
// WikipediaKit
//
// Created by Frank Rausch on 2017-03-21.
// Copyright © 2017 Raureif GmbH / Frank Rausch
// Created by Frank Rausch on 2020-09-01.
// Copyright © 2020 Raureif GmbH / Frank Rausch
//
// MIT License
//
Expand Down Expand Up @@ -35,82 +35,56 @@ extension Wikipedia {
title: String,
fragment: String? = nil,
imageWidth: Int,
completion: @escaping (WikipediaArticle?, WikipediaError?)->())
completion: @escaping (Result<WikipediaArticle, WikipediaError>)->())
-> URLSessionDataTask? {


if let cachedArticle = self.articleCache.get(language: language, title: title) {
DispatchQueue.main.async {
completion(cachedArticle, nil)
completion(.success(cachedArticle))
}
return nil
}

let parameters: [String:String] = [
"action": "mobileview",
"format": "json",
"page": title,
"mobileformat": "1",
"prop": "id|text|sections|languagecount|displaytitle|description|image|thumb|pageprops",
"sections": "all",
"sectionprop": "toclevel|level|line|anchor",
"thumbwidth" : "\(imageWidth)",
"redirect": "yes",
"maxage": "\(self.maxAgeInSeconds)",
"smaxage": "\(self.maxAgeInSeconds)",
"uselang": language.variant ?? language.code,
]

guard let request = Wikipedia.buildURLRequest(language: language, parameters: parameters) else {

let title = title.wikipediaURLEncodedString(encodeSlashes: true)

let urlString = "https://\(language.code).wikipedia.org/api/rest_v1/page/mobile-sections/\(title)"

guard let url = URL(string: urlString)
else {
DispatchQueue.main.async {
completion(nil, .other(nil))
completion(.failure(.other(nil)))
}
return nil
}


let request = URLRequest(url: url)

return WikipediaNetworking.shared.loadJSON(urlRequest: request) { jsonDictionary, error in

guard error == nil else {
DispatchQueue.main.async {
completion (nil, error)
completion (.failure(error!))
}
return
}

guard let jsonDictionary = jsonDictionary else {
DispatchQueue.main.async {
completion (nil, .decodingError)
completion (.failure(.decodingError))
}
return
}

if let apiError = jsonDictionary["error"] as? JSONDictionary,
let apiErrorInfo = apiError["info"] as? String {

var wikipediaError: WikipediaError

if let apiErrorCode = apiError["code"] as? String,
apiErrorCode == "missingtitle" {

wikipediaError = .notFound
} else {
wikipediaError = .apiError(apiErrorInfo)

if let article = WikipediaArticle(jsonDictionary: jsonDictionary, language: language, title: title, fragment: fragment, imageWidth: imageWidth) {
self.articleCache.add(article)
DispatchQueue.main.async {
completion(.success(article))
}
} else {
DispatchQueue.main.async {
completion (nil, wikipediaError)
completion(.failure(.decodingError))
}
return
}

let article = WikipediaArticle(jsonDictionary: jsonDictionary, language: language, title: title, fragment: fragment)

if let article = article {
self.articleCache.add(article)
}

DispatchQueue.main.async {
completion(article, error)
}
}
}
Expand Down
Loading

0 comments on commit 5253906

Please sign in to comment.