Skip to content

Commit 49248a4

Browse files
committed
Rework endpoints and naming
1 parent d091280 commit 49248a4

14 files changed

Lines changed: 166 additions & 73 deletions

Sources/FairExpo/AltStoreCatalog.swift

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,18 @@ public struct AltCatalogAppItem : Codable, Equatable {
9090
public var subtitle: String?
9191
/// A full-length description of your app. This can include any information you believe is relevant for your app, such as feature descriptions or additional links.
9292
public var localizedDescription: String?
93-
/// Undocumented, but used by
94-
public var _localizedDescription: [String: String]?
93+
/// Undocumented, e.g.: `{ "en": "English description", "fr": "Description en français" }`
94+
public var _localizedDescriptions: [String: String]?
9595
/// A link to you app's icon image. It will automatically be masked to an app icon shape.
9696
public var iconURL: String?
9797
/// The color used to theme your app's store page. We recommend using your app's existing tint color (if it has one), but you are free to choose any color you want.
9898
public var tintColor: String?
9999
/// The store category best representing your app.
100-
public var category: String?
100+
public var category: AltStoreCategory?
101101
/// Screenshots of your app. We recommend showcasing your app's main features.
102102
public var screenshots: ScreenshotCollection?
103+
/// Undocumented
104+
public var beta: Bool?
103105
/// A list of all the published versions of your app.
104106
public var versions: [AltCatalogAppItemVersion]?
105107
/// An object listing all entitlements and privacy permissions information used by the app.
@@ -112,7 +114,7 @@ public struct AltCatalogAppItem : Codable, Equatable {
112114
public typealias ScreenshotCollection = Either<[ScreenshotChoice]>.Or<[String: [ScreenshotChoice]]>
113115
public typealias ScreenshotChoice = Either<String>.Or<AppScreenshot>
114116

115-
public init(name: String, bundleIdentifier: String? = nil, marketplaceID: String? = nil, developerName: String? = nil, subtitle: String? = nil, localizedDescription: String? = nil, iconURL: String? = nil, tintColor: String? = nil, category: String? = nil, screenshots: ScreenshotCollection? = nil, versions: [AltCatalogAppItemVersion]? = nil, appPermissions: AltCatalogAppItemPermissions? = nil, patreon: AltCatalogAppItemPatreon? = nil) {
117+
public init(name: String, bundleIdentifier: String? = nil, marketplaceID: String? = nil, developerName: String? = nil, subtitle: String? = nil, localizedDescription: String? = nil, iconURL: String? = nil, tintColor: String? = nil, category: AltStoreCategory? = nil, screenshots: ScreenshotCollection? = nil, versions: [AltCatalogAppItemVersion]? = nil, appPermissions: AltCatalogAppItemPermissions? = nil, patreon: AltCatalogAppItemPatreon? = nil) {
116118
self.name = name
117119
self.bundleIdentifier = bundleIdentifier
118120
self.marketplaceID = marketplaceID
@@ -129,6 +131,37 @@ public struct AltCatalogAppItem : Codable, Equatable {
129131
}
130132
}
131133

134+
/// The store category best representing your app. One of: `developer`, `entertainment`, `games`, `lifestyle`, `other`, `photo-video`, `social`, `utilities`
135+
///
136+
/// https://faq.altstore.io/developers/make-a-source#category-string
137+
public struct AltStoreCategory : Codable, Equatable, RawRepresentable, CaseIterable {
138+
public static let developer = AltStoreCategory(rawValue: "developer")
139+
public static let entertainment = AltStoreCategory(rawValue: "entertainment")
140+
public static let games = AltStoreCategory(rawValue: "games")
141+
public static let lifestyle = AltStoreCategory(rawValue: "lifestyle")
142+
public static let other = AltStoreCategory(rawValue: "other")
143+
public static let photoVideo = AltStoreCategory(rawValue: "photo-video")
144+
public static let social = AltStoreCategory(rawValue: "social")
145+
public static let utilities = AltStoreCategory(rawValue: "utilities")
146+
147+
public static var allCases: [AltStoreCategory] = [
148+
.developer,
149+
.entertainment,
150+
.games,
151+
.lifestyle,
152+
.other,
153+
.photoVideo,
154+
.social,
155+
.utilities,
156+
]
157+
158+
public var rawValue: String
159+
160+
public init(rawValue: String) {
161+
self.rawValue = rawValue
162+
}
163+
}
164+
132165
/// https://faq.altstore.io/developers/make-a-source#screenshots
133166
public struct AppScreenshot : Codable, Equatable {
134167
public var imageURL: String

Sources/FairExpo/AppCatalogAPI.swift

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,44 @@
11
import Foundation
22
import FairCore
33

4+
extension AppCatalogIndex {
5+
public static let appfairCatalogURL: URL! = URL(string: "https://appfair.net/appfair-apps.json")
6+
}
7+
8+
9+
/// The master list of apps and configuration for an app catalog.
10+
///
11+
/// This merely includes fundamental pointers to the app's location, like the app name and token and bundleID/appid.
12+
/// The remainder of the metadata will be fetched from the specific catalog (e.g., `altstore.json`, `fdroid.json`)
13+
/// attached to individual releases.
14+
///
15+
/// See: https://appfair.net/appfair-apps.json
16+
public struct AppCatalogIndex : Decodable {
17+
public var catalogs: Catalogs
18+
public var apps: [App]
19+
20+
public struct Catalogs : Decodable {
21+
public var altstore: AltCatalog?
22+
public var fdroid: FDroidIndex.Repo?
23+
}
24+
25+
public struct App : Decodable {
26+
public var token: String // e.g., "Skip-Notes"
27+
public var ios: DarwinApp?
28+
public var android: AndroidApp?
29+
30+
public struct DarwinApp : Decodable {
31+
public var bundleId: String
32+
public var appleItemId: String?
33+
}
34+
35+
public struct AndroidApp : Decodable {
36+
public var appid: String
37+
}
38+
}
39+
}
40+
41+
442
extension Plist {
543
/// A map of all the "*UsageDescription*" properties that have string values
644
var usageDescriptions: [String: String] {
@@ -144,8 +182,6 @@ public struct LocalizedStringsFile {
144182
}
145183
}
146184

147-
148-
149185
/// https://docs.fastlane.tools/actions/deliver/#available-metadata-folder-options
150186
public struct AppMetadata : Codable {
151187
// Non-Localized Metadata

Sources/FairExpo/FDroidCatalog.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ public struct FDroidIndex : Codable, Equatable {
154154
}
155155

156156
/// A categorization of an app (e.g, "Connectivity", "Development", "Games", "Graphics", "Internet", "Money", "Multimedia", "Navigation", "Phone & SMS", "Reading", "Science & Education", "Security", "Sports & Health", "System", "Theming", "Time", "Writing")
157+
///
158+
/// e.g., see the list at https://gitlab.com/fdroid/fdroiddata/-/blob/master/config/categories.yml
157159
public struct Category : Codable, Equatable {
158160
public var icon: LocalizedFile?
159161
public var name: LocalizedText?

Sources/FairExpo/FairSeal.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import FairCore
55
public enum FairGround {
66
/// A fairground that uses hosted git repository with a REST API,
77
/// such as github.com
8-
case hub(FairHub)
8+
case hub(GitHubEndpointService)
99

10-
/// The `FairHub` for repositories that use that model
11-
public var hub: FairHub? {
10+
/// The `GitHubEndpointService` for repositories that use that model
11+
public var hub: GitHubEndpointService? {
1212
switch self {
1313
case .hub(let x): return x
1414
}

Sources/FairExpo/HubAPI.swift renamed to Sources/FairExpo/GitHubEndpointService.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ public let appfairName = "appfair"
1010

1111
public let appfairRoot = URL(string: "https://appfair.net")!
1212

13+
@available(*, deprecated, renamed: "GitHubEndpointService")
14+
public typealias FairHub = GitHubEndpointService
15+
1316
/// A Fair Ground based on an online git service such as GitHub or GitLab.
14-
public struct FairHub : GraphQLEndpointService {
17+
public struct GitHubEndpointService : GraphQLEndpointService {
1518
/// The root of the FairGround-compatible service
1619
public var baseURL: URL
1720

@@ -34,7 +37,7 @@ public struct FairHub : GraphQLEndpointService {
3437
/// 502 sometimes happens with large responses
3538
public static var backoffCodes: IndexSet { IndexSet([403, 502]) }
3639

37-
public typealias BaseFork = FairHub.CatalogForksQuery.QueryResponse.BaseRepository.Repository
40+
public typealias BaseFork = CatalogForksQuery.QueryResponse.BaseRepository.Repository
3841

3942
/// The FairHub is initialized with a host identifier (e.g., "github.com/appfair") that corresponds to the hub being used.
4043
public init(hostOrg: String, authToken: String? = nil, fairsealIssuer: String?, fairsealKey: Data?, requestRetryCount: Int) throws {
@@ -76,7 +79,7 @@ public struct ArtifactTarget : Codable, Hashable {
7679
}
7780
}
7881

79-
extension FairHub {
82+
extension GitHubEndpointService {
8083
public struct ProjectConfiguration {
8184
/// The regular expression patterns of allowed app names
8285
public var allowName: [NSRegularExpression]
@@ -106,7 +109,7 @@ extension FairHub {
106109
/// Validates that the app name is included in the `allow-name` patterns and not included in the `deny-name` list of expressions.
107110
public func validateAppName(_ name: String?) throws {
108111
guard let name = name, try permitted(value: name, allow: allowName, deny: denyName) == true else {
109-
throw FairHub.Errors.invalidName(name)
112+
throw Errors.invalidName(name)
110113
}
111114
}
112115

@@ -118,14 +121,14 @@ extension FairHub {
118121
// if we specified an allow list, then at least one of the patterns must match the email
119122
if !allow.isEmpty {
120123
guard let _ = allow.first(where: matches) else {
121-
throw FairHub.Errors.valueNotAllowed(value)
124+
throw Errors.valueNotAllowed(value)
122125
}
123126
}
124127

125128
// conversely, if we specified a deny list, then all the addresses must not match
126129
if !deny.isEmpty {
127130
if let _ = deny.first(where: matches) {
128-
throw FairHub.Errors.valueDenied(value)
131+
throw Errors.valueDenied(value)
129132
}
130133
}
131134

@@ -144,7 +147,7 @@ extension FairHub {
144147
public func buildFundingSources(owner: String, baseRepository: String) async throws -> [AppFundingSource] {
145148
var sources: [AppFundingSource] = []
146149

147-
func createSponsor(from sponsor: FairHub.GetSponsorsQuery.QueryResponse.Repository.SponsorsListing, url: URL) -> AppFundingSource {
150+
func createSponsor(from sponsor: GetSponsorsQuery.QueryResponse.Repository.SponsorsListing, url: URL) -> AppFundingSource {
148151
var goals: [AppFundingSource.FundingGoal] = []
149152
if let activeGoal = sponsor.activeGoal,
150153
let goalKind = activeGoal.kind?.rawValue {
@@ -282,7 +285,7 @@ extension FairHub {
282285
let nameWithOwner = appOrg + "/" + baseRepository
283286

284287
let prid = try await fetchPRID()
285-
func fetchPRID() async throws -> FairHub.OID {
288+
func fetchPRID() async throws -> GitHubEndpointService.OID {
286289
if let issueNumber = issueNumber {
287290
// the issue number was explicitly specified; look up the PR issue by number
288291
return try await self.request(LookupPRNumberQuery(owner: owner, name: baseRepository, prid: issueNumber)).get().data.repository.pullRequest.id

0 commit comments

Comments
 (0)