From 3d5afbd589b2fe636aa305cb9c88dff5e1a31976 Mon Sep 17 00:00:00 2001 From: sunxiaojian Date: Tue, 26 Jan 2021 17:38:30 +0800 Subject: [PATCH 1/3] feat: add MastodonAPI --- Mastodon.xcodeproj/project.pbxproj | 7 ++ .../xcshareddata/swiftpm/Package.resolved | 27 +++++++ Mastodon/ViewController.swift | 4 +- MastodonSDK/Package.swift | 9 ++- .../MastodonSDK/API/Mastodon+API+App.swift | 60 +++++++++++++++ .../MastodonSDK/API/Mastodon+API.swift | 73 +++++++++++++++++++ .../MastodonSDK/Entity/Mastodon+Entity.swift | 9 +++ .../Sources/MastodonSDK/Mastodon.swift | 15 ++++ .../Sources/MastodonSDK/MastodonSDK.swift | 4 +- 9 files changed, 201 insertions(+), 7 deletions(-) create mode 100644 MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+App.swift create mode 100644 MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift create mode 100644 MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity.swift create mode 100644 MastodonSDK/Sources/MastodonSDK/Mastodon.swift diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 01a6fa1b7..75888b09e 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 3533495136D843E85211E3E2 /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1B4523A7981F1044DE89C21 /* Pods_Mastodon_MastodonUITests.framework */; }; + 5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 5D526FE125BE9AC400460CB9 /* MastodonSDK */; }; 5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; }; 7A9135D4559750AF07CA9BE4 /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 602D783BEC22881EBAD84419 /* Pods_Mastodon.framework */; }; DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = DB3D0FF225BAA61700EAA174 /* AlamofireImage */; }; @@ -76,6 +77,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */, 7A9135D4559750AF07CA9BE4 /* Pods_Mastodon.framework in Frameworks */, DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */, ); @@ -226,6 +228,7 @@ name = Mastodon; packageProductDependencies = ( DB3D0FF225BAA61700EAA174 /* AlamofireImage */, + 5D526FE125BE9AC400460CB9 /* MastodonSDK */, ); productName = Mastodon; productReference = DB427DD225BAA00100D1B89D /* Mastodon.app */; @@ -830,6 +833,10 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 5D526FE125BE9AC400460CB9 /* MastodonSDK */ = { + isa = XCSwiftPackageProductDependency; + productName = MastodonSDK; + }; DB3D0FF225BAA61700EAA174 /* AlamofireImage */ = { isa = XCSwiftPackageProductDependency; package = DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */; diff --git a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8842af1ee..17197be49 100644 --- a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -18,6 +18,33 @@ "revision": "3e8edbeb75227f8542aa87f90240cf0424d6362f", "version": "4.1.0" } + }, + { + "package": "swift-nio", + "repositoryURL": "https://github.com/apple/swift-nio.git", + "state": { + "branch": null, + "revision": "8da5c5a4e6c5084c296b9f39dc54f00be146e0fa", + "version": "1.14.2" + } + }, + { + "package": "swift-nio-zlib-support", + "repositoryURL": "https://github.com/apple/swift-nio-zlib-support.git", + "state": { + "branch": null, + "revision": "37760e9a52030bb9011972c5213c3350fa9d41fd", + "version": "1.0.0" + } + }, + { + "package": "SwiftyJSON", + "repositoryURL": "https://github.com/SwiftyJSON/SwiftyJSON.git", + "state": { + "branch": null, + "revision": "2b6054efa051565954e1d2b9da831680026cd768", + "version": "5.0.0" + } } ] }, diff --git a/Mastodon/ViewController.swift b/Mastodon/ViewController.swift index 235112a12..a90e181b7 100644 --- a/Mastodon/ViewController.swift +++ b/Mastodon/ViewController.swift @@ -6,11 +6,13 @@ // import UIKit +import MastodonSDK class ViewController: UIViewController { - + override func viewDidLoad() { super.viewDidLoad() + // Do any additional setup after loading the view. } diff --git a/MastodonSDK/Package.swift b/MastodonSDK/Package.swift index 61755106c..47589f32c 100644 --- a/MastodonSDK/Package.swift +++ b/MastodonSDK/Package.swift @@ -14,15 +14,18 @@ let package = Package( targets: ["MastodonSDK"]), ], dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), + .package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "5.0.0"), + .package(url: "https://github.com/apple/swift-nio.git", from: "1.0.0"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "MastodonSDK", - dependencies: [] + dependencies: [ + .product(name: "SwiftyJSON", package: "SwiftyJSON"), + .product(name: "NIOHTTP1", package: "swift-nio"), + ] ), .testTarget( name: "MastodonSDKTests", diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+App.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+App.swift new file mode 100644 index 000000000..7d31280a5 --- /dev/null +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+App.swift @@ -0,0 +1,60 @@ +// +// Mastodon+API+App.swift +// +// +// Created by xiaojian sun on 2021/1/25. +// + +import Combine +import Foundation + +public extension Mastodon.API.App { + internal static let appEndpointURL = Mastodon.API.endpointURL.appendingPathComponent("apps") + + struct OAuth2Credentials: Codable { + public let id: String + + public let name: String + public let website: String? + public let redirectURI: String + public let clientID: String + public let clientSecret: String + public let vapidKey: String + + enum CodingKeys: String, CodingKey { + case id + + case name + case website + case redirectURI = "redirect_uri" + case clientID = "client_id" + case clientSecret = "client_secret" + case vapidKey = "vapid_key" + } + } + + struct registerAppQuery { + public let client_name: String + public let redirect_uris: String + public let scopes: String + public let website: String + + public init(client_name: String, redirect_uris: String, scopes: String, website: String) { + self.client_name = client_name + self.redirect_uris = redirect_uris + self.scopes = scopes + self.website = website + } + + var queryItems: [URLQueryItem]? { + var items: [URLQueryItem] = [] + items.append(URLQueryItem(name: "client_name", value: client_name)) + items.append(URLQueryItem(name: "redirect_uris", value: redirect_uris)) + items.append(URLQueryItem(name: "scopes", value: scopes)) + guard !items.isEmpty else { return nil } + return items + } + } +} + +extension Mastodon.API.App.OAuth2Credentials: Equatable {} diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift new file mode 100644 index 000000000..cf25fb091 --- /dev/null +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift @@ -0,0 +1,73 @@ +// +// Mastodon+API.swift +// +// +// Created by xiaojian sun on 2021/1/25. +// + +import Foundation +import NIOHTTP1 + +public extension Mastodon.API { + static var baseUrl = "" + static let endpointURL = URL(string: baseUrl + "/api/v1/")! + + static let timeoutInterval: TimeInterval = 10 + static let decoder: JSONDecoder = { + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .MastodonStrategy + return decoder + }() + + static let httpHeaderDateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz" + return formatter + }() + + enum App {} +} + +extension Mastodon.API { + // Error Response when request V1 endpoint + struct ErrorResponse: Codable { + let errors: [ErrorDescription] + + struct ErrorDescription: Codable { + public let code: Int + public let message: String + } + } + + // Alternative Error Response when request V1 endpoint + struct ErrorRequestResponse: Codable { + let request: String + let error: String + } +} + +extension Mastodon.API { + +} + +private extension JSONDecoder.DateDecodingStrategy { + static let MastodonStrategy = custom { decoder throws -> Date in + let container = try decoder.singleValueContainer() + let string = try container.decode(String.self) + + let formatterV1 = DateFormatter() + formatterV1.locale = Locale(identifier: "en") + formatterV1.dateFormat = "EEE MMM dd HH:mm:ss ZZZZZ yyyy" + if let date = formatterV1.date(from: string) { + return date + } + + let formatterV2 = ISO8601DateFormatter() + formatterV2.formatOptions.insert(.withFractionalSeconds) + if let date = formatterV2.date(from: string) { + return date + } + + throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)") + } +} diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity.swift new file mode 100644 index 000000000..761f2b0ed --- /dev/null +++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity.swift @@ -0,0 +1,9 @@ +// +// File.swift +// +// +// Created by xiaojian sun on 2021/1/25. +// + +import Foundation +extension Mastodon.Entity { } diff --git a/MastodonSDK/Sources/MastodonSDK/Mastodon.swift b/MastodonSDK/Sources/MastodonSDK/Mastodon.swift new file mode 100644 index 000000000..da1cdd7a7 --- /dev/null +++ b/MastodonSDK/Sources/MastodonSDK/Mastodon.swift @@ -0,0 +1,15 @@ +// +// File.swift +// +// +// Created by xiaojian sun on 2021/1/25. +// + +import Foundation + +public enum Mastodon { + public enum Request { } + public enum Response { } + public enum API { } + public enum Entity { } +} diff --git a/MastodonSDK/Sources/MastodonSDK/MastodonSDK.swift b/MastodonSDK/Sources/MastodonSDK/MastodonSDK.swift index 89c41049e..8b1378917 100644 --- a/MastodonSDK/Sources/MastodonSDK/MastodonSDK.swift +++ b/MastodonSDK/Sources/MastodonSDK/MastodonSDK.swift @@ -1,3 +1 @@ -struct MastodonSDK { - var text = "Hello, World!" -} + From 65d6ae6e11614916f515447f2ebe1752be4dd827 Mon Sep 17 00:00:00 2001 From: CMK Date: Tue, 26 Jan 2021 18:11:44 +0800 Subject: [PATCH 2/3] chore: rename and cleanup --- .../MastodonSDK/API/Mastodon+API+App.swift | 39 +++++++----- .../MastodonSDK/API/Mastodon+API+Error.swift | 22 +++++++ .../MastodonSDK/API/Mastodon+API.swift | 61 +++---------------- .../MastodonSDK/Entity/Mastodon+Entity.swift | 3 +- .../Sources/MastodonSDK/Mastodon.swift | 2 +- .../Sources/MastodonSDK/MastodonSDK.swift | 1 - .../MastodonSDKTests/MastodonSDKTests.swift | 1 - 7 files changed, 58 insertions(+), 71 deletions(-) create mode 100644 MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Error.swift delete mode 100644 MastodonSDK/Sources/MastodonSDK/MastodonSDK.swift diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+App.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+App.swift index 7d31280a5..5bffa018f 100644 --- a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+App.swift +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+App.swift @@ -9,9 +9,13 @@ import Combine import Foundation public extension Mastodon.API.App { - internal static let appEndpointURL = Mastodon.API.endpointURL.appendingPathComponent("apps") - struct OAuth2Credentials: Codable { + static func appEndpointURL(domain: String) -> URL { + return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("apps") + } + + struct Application: Codable { + public let id: String public let name: String @@ -33,28 +37,33 @@ public extension Mastodon.API.App { } } - struct registerAppQuery { - public let client_name: String - public let redirect_uris: String - public let scopes: String - public let website: String + struct CreateAnAppQuery { + public let clientName: String + public let redirectURIs: String + public let scopes: String? + public let website: String? - public init(client_name: String, redirect_uris: String, scopes: String, website: String) { - self.client_name = client_name - self.redirect_uris = redirect_uris + public init(clientName: String, redirectURIs: String, scopes: String?, website: String?) { + self.clientName = clientName + self.redirectURIs = redirectURIs self.scopes = scopes self.website = website } var queryItems: [URLQueryItem]? { var items: [URLQueryItem] = [] - items.append(URLQueryItem(name: "client_name", value: client_name)) - items.append(URLQueryItem(name: "redirect_uris", value: redirect_uris)) - items.append(URLQueryItem(name: "scopes", value: scopes)) + items.append(URLQueryItem(name: "client_name", value: clientName)) + items.append(URLQueryItem(name: "redirect_uris", value: redirectURIs)) + scopes.flatMap { + items.append(URLQueryItem(name: "scopes", value: $0)) + } + website.flatMap { + items.append(URLQueryItem(name: "website", value: $0)) + } + guard !items.isEmpty else { return nil } return items } } + } - -extension Mastodon.API.App.OAuth2Credentials: Equatable {} diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Error.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Error.swift new file mode 100644 index 000000000..7a6c5e899 --- /dev/null +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Error.swift @@ -0,0 +1,22 @@ +// +// Mastodon+API+Error.swift +// +// +// Created by MainasuK Cirno on 2021/1/26. +// + +import Foundation + +extension Mastodon.API.Error { + + struct ErrorResponse: Codable { + let error: String + let errorDescription: String? + + enum CodingKeys: String, CodingKey { + case error + case errorDescription = "error_description" + } + } + +} diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift index cf25fb091..e83f29fb5 100644 --- a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift @@ -9,65 +9,22 @@ import Foundation import NIOHTTP1 public extension Mastodon.API { - static var baseUrl = "" - static let endpointURL = URL(string: baseUrl + "/api/v1/")! + + static func endpointURL(domain: String) -> URL { + return URL(string: "https://" + domain + "/api/v1/")! + } static let timeoutInterval: TimeInterval = 10 static let decoder: JSONDecoder = { let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .MastodonStrategy + decoder.dateDecodingStrategy = .iso8601 + return decoder }() - static let httpHeaderDateFormatter: DateFormatter = { - let formatter = DateFormatter() - formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz" - return formatter - }() + static let httpHeaderDateFormatter = ISO8601DateFormatter() - enum App {} -} - -extension Mastodon.API { - // Error Response when request V1 endpoint - struct ErrorResponse: Codable { - let errors: [ErrorDescription] - - struct ErrorDescription: Codable { - public let code: Int - public let message: String - } - } + enum Error { } + enum App { } - // Alternative Error Response when request V1 endpoint - struct ErrorRequestResponse: Codable { - let request: String - let error: String - } -} - -extension Mastodon.API { - -} - -private extension JSONDecoder.DateDecodingStrategy { - static let MastodonStrategy = custom { decoder throws -> Date in - let container = try decoder.singleValueContainer() - let string = try container.decode(String.self) - - let formatterV1 = DateFormatter() - formatterV1.locale = Locale(identifier: "en") - formatterV1.dateFormat = "EEE MMM dd HH:mm:ss ZZZZZ yyyy" - if let date = formatterV1.date(from: string) { - return date - } - - let formatterV2 = ISO8601DateFormatter() - formatterV2.formatOptions.insert(.withFractionalSeconds) - if let date = formatterV2.date(from: string) { - return date - } - - throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)") - } } diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity.swift index 761f2b0ed..cb8cf240f 100644 --- a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity.swift +++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity.swift @@ -1,9 +1,10 @@ // -// File.swift +// Mastodon+Entity.swift // // // Created by xiaojian sun on 2021/1/25. // import Foundation + extension Mastodon.Entity { } diff --git a/MastodonSDK/Sources/MastodonSDK/Mastodon.swift b/MastodonSDK/Sources/MastodonSDK/Mastodon.swift index da1cdd7a7..0c5e90609 100644 --- a/MastodonSDK/Sources/MastodonSDK/Mastodon.swift +++ b/MastodonSDK/Sources/MastodonSDK/Mastodon.swift @@ -1,5 +1,5 @@ // -// File.swift +// Mastodon.swift // // // Created by xiaojian sun on 2021/1/25. diff --git a/MastodonSDK/Sources/MastodonSDK/MastodonSDK.swift b/MastodonSDK/Sources/MastodonSDK/MastodonSDK.swift deleted file mode 100644 index 8b1378917..000000000 --- a/MastodonSDK/Sources/MastodonSDK/MastodonSDK.swift +++ /dev/null @@ -1 +0,0 @@ - diff --git a/MastodonSDK/Tests/MastodonSDKTests/MastodonSDKTests.swift b/MastodonSDK/Tests/MastodonSDKTests/MastodonSDKTests.swift index 85e781813..ff273005c 100644 --- a/MastodonSDK/Tests/MastodonSDKTests/MastodonSDKTests.swift +++ b/MastodonSDK/Tests/MastodonSDKTests/MastodonSDKTests.swift @@ -6,7 +6,6 @@ final class MastodonSDKTests: XCTestCase { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct // results. - XCTAssertEqual(MastodonSDK().text, "Hello, World!") } static var allTests = [ From 3a7c45344eff36ff143df6ca1e72393d854da8fa Mon Sep 17 00:00:00 2001 From: CMK Date: Tue, 26 Jan 2021 19:33:16 +0800 Subject: [PATCH 3/3] chore: update README --- README.md | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7dcb338db..948ddf48a 100644 --- a/README.md +++ b/README.md @@ -3,18 +3,43 @@ ## Requirements +- Xcode 12.4+ +- Swift 5.3+ +- iOS 14.0+ + ## Setup +We needs the latest version Xcode from App Store. And install Cocoapods for dependency management. -### Setup for M1 Mac +### CocoaPods -CocoaPods may fail to update. Try install fii to fix it. +#### For the Intel Mac ```zsh -# https://github.com/CocoaPods/CocoaPods/issues/10220 +# install cocoapods from Homebrew +brew install cocoapods +pod install +``` + +#### For the M1 Mac + +```zsh +# install cocoapods from Homebrew +brew install cocoapods + +# pod install may not works on M1 Mac. Fix by install ffi +# ref: https://github.com/CocoaPods/CocoaPods/issues/10220 sudo arch -x86_64 gem install ffi +arch -x86_64 pod install ``` +## Start + +1. Open `Mastodon.xcworkspace` +2. Wait the Swift Package Dependencies resolved. +2. Check the signing settings make sure choose a team. [More info…](https://help.apple.com/xcode/mac/current/#/dev23aab79b4) +3. Select `Mastodon` scheme and run it. + ## Acknowledgements