From 9b3cc61dcbcedd83d0c9debb1457b1adecac4048 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sat, 9 Jul 2022 11:45:27 -0400 Subject: [PATCH] Update WebURL to version with IDNA support Closes #163 --- Pachyderm/Package.swift | 2 +- .../Sources/Pachyderm/Model/Hashtag.swift | 18 ++++-------------- .../Sources/Pachyderm/Model/Mention.swift | 15 ++------------- Pachyderm/Tests/PachydermTests/URLTests.swift | 11 ++++++++--- Tusker/CoreData/SavedHashtag.swift | 3 ++- .../Explore/ExploreViewController.swift | 6 +++++- .../TrendingHashtagsViewController.swift | 6 ++++-- .../Screens/Search/SearchViewController.swift | 6 +++++- Tusker/Screens/Utilities/Previewing.swift | 8 +++++++- Tusker/Views/StatusContentTextView.swift | 5 +++-- 10 files changed, 41 insertions(+), 39 deletions(-) diff --git a/Pachyderm/Package.swift b/Pachyderm/Package.swift index 234c48fc..7f1a9a91 100644 --- a/Pachyderm/Package.swift +++ b/Pachyderm/Package.swift @@ -16,7 +16,7 @@ let package = Package( ], dependencies: [ // Dependencies declare other packages that this package depends on. - .package(url: "https://github.com/karwa/swift-url.git", from: "0.3.1"), + .package(url: "https://github.com/karwa/swift-url.git", branch: "main"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. diff --git a/Pachyderm/Sources/Pachyderm/Model/Hashtag.swift b/Pachyderm/Sources/Pachyderm/Model/Hashtag.swift index ad046701..961c7c06 100644 --- a/Pachyderm/Sources/Pachyderm/Model/Hashtag.swift +++ b/Pachyderm/Sources/Pachyderm/Model/Hashtag.swift @@ -12,13 +12,13 @@ import WebURLFoundationExtras public class Hashtag: Codable { public let name: String - public let url: URL + public let url: WebURL /// Only present when returned from the trending hashtags endpoint public let history: [History]? public init(name: String, url: URL) { self.name = name - self.url = url + self.url = WebURL(url)! self.history = nil } @@ -26,24 +26,14 @@ public class Hashtag: Codable { let container = try decoder.container(keyedBy: CodingKeys.self) self.name = try container.decode(String.self, forKey: .name) // pixelfed (possibly others) don't fully escape special characters in the hashtag url - do { - let webURL = try container.decode(WebURL.self, forKey: .url) - if let url = URL(webURL) { - self.url = url - } else { - let s = try? container.decode(String.self, forKey: .url) - throw DecodingError.dataCorruptedError(forKey: .url, in: container, debugDescription: "unable to convert WebURL \(s?.debugDescription ?? "nil") to URL") - } - } catch { - self.url = try container.decode(URL.self, forKey: .url) - } + self.url = try container.decode(WebURL.self, forKey: .url) self.history = try container.decodeIfPresent([History].self, forKey: .history) } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(name, forKey: .name) - try container.encode(url.absoluteString, forKey: .url) + try container.encode(url, forKey: .url) try container.encodeIfPresent(history, forKey: .history) } diff --git a/Pachyderm/Sources/Pachyderm/Model/Mention.swift b/Pachyderm/Sources/Pachyderm/Model/Mention.swift index 84f4ca3a..49a58cc5 100644 --- a/Pachyderm/Sources/Pachyderm/Model/Mention.swift +++ b/Pachyderm/Sources/Pachyderm/Model/Mention.swift @@ -8,10 +8,9 @@ import Foundation import WebURL -import WebURLFoundationExtras public class Mention: Codable { - public let url: URL + public let url: WebURL public let username: String public let acct: String /// The instance-local ID of the user being mentioned. @@ -22,17 +21,7 @@ public class Mention: Codable { self.username = try container.decode(String.self, forKey: .username) self.acct = try container.decode(String.self, forKey: .acct) self.id = try container.decode(String.self, forKey: .id) - do { - let webURL = try container.decode(WebURL.self, forKey: .url) - if let url = URL(webURL) { - self.url = url - } else { - let s = try? container.decode(String.self, forKey: .url) - throw DecodingError.dataCorruptedError(forKey: .url, in: container, debugDescription: "unable to convert WebURL \(s?.debugDescription ?? "nil") to URL") - } - } catch { - self.url = try container.decode(URL.self, forKey: .url) - } + self.url = try container.decode(WebURL.self, forKey: .url) } private enum CodingKeys: String, CodingKey { diff --git a/Pachyderm/Tests/PachydermTests/URLTests.swift b/Pachyderm/Tests/PachydermTests/URLTests.swift index 4ac4344a..7564c398 100644 --- a/Pachyderm/Tests/PachydermTests/URLTests.swift +++ b/Pachyderm/Tests/PachydermTests/URLTests.swift @@ -12,9 +12,14 @@ import WebURLFoundationExtras class URLTests: XCTestCase { func testDecodeURL() { - print(WebURL(URL(string: "https://xn--baw-joa.social/@unituebingen")!)) - let url = WebURL("https://xn--baw-joa.social/@unituebingen") - print(url) + XCTAssertNotNil(WebURL(URL(string: "https://xn--baw-joa.social/@unituebingen")!)) + XCTAssertNotNil(WebURL("https://xn--baw-joa.social/@unituebingen")) + XCTAssertNotNil(URLComponents(string: "https://xn--baw-joa.social/test/é")) + XCTAssertNotNil(WebURL("https://xn--baw-joa.social/test/é")) + if #available(iOS 16.0, *) { + XCTAssertNotNil(try? URL.ParseStrategy().parse("https://xn--baw-joa.social/test/é")) + XCTAssertNotNil(try? URL.ParseStrategy().parse("http://見.香港/热狗/🌭")) + } } } diff --git a/Tusker/CoreData/SavedHashtag.swift b/Tusker/CoreData/SavedHashtag.swift index f4e7bab9..12a3037d 100644 --- a/Tusker/CoreData/SavedHashtag.swift +++ b/Tusker/CoreData/SavedHashtag.swift @@ -9,6 +9,7 @@ import Foundation import CoreData import Pachyderm +import WebURLFoundationExtras @objc(SavedHashtag) public final class SavedHashtag: NSManagedObject { @@ -32,6 +33,6 @@ extension SavedHashtag { convenience init(hashtag: Hashtag, context: NSManagedObjectContext) { self.init(context: context) self.name = hashtag.name - self.url = hashtag.url + self.url = URL(hashtag.url)! } } diff --git a/Tusker/Screens/Explore/ExploreViewController.swift b/Tusker/Screens/Explore/ExploreViewController.swift index a4cb8f5e..d7cd8632 100644 --- a/Tusker/Screens/Explore/ExploreViewController.swift +++ b/Tusker/Screens/Explore/ExploreViewController.swift @@ -10,6 +10,7 @@ import UIKit import Combine import Pachyderm import CoreData +import WebURLFoundationExtras class ExploreViewController: UIViewController, UICollectionViewDelegate { @@ -582,7 +583,10 @@ extension ExploreViewController: UICollectionViewDragDelegate { activity.displaysAuxiliaryScene = true provider = NSItemProvider(object: activity) case let .savedHashtag(hashtag): - provider = NSItemProvider(object: hashtag.url as NSURL) + guard let url = URL(hashtag.url) else { + return [] + } + provider = NSItemProvider(object: url as NSURL) if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: accountID) { activity.displaysAuxiliaryScene = true provider.registerObject(activity, visibility: .all) diff --git a/Tusker/Screens/Explore/TrendingHashtagsViewController.swift b/Tusker/Screens/Explore/TrendingHashtagsViewController.swift index a48f5266..ce67bd48 100644 --- a/Tusker/Screens/Explore/TrendingHashtagsViewController.swift +++ b/Tusker/Screens/Explore/TrendingHashtagsViewController.swift @@ -8,6 +8,7 @@ import UIKit import Pachyderm +import WebURLFoundationExtras class TrendingHashtagsViewController: UIViewController { @@ -105,10 +106,11 @@ extension TrendingHashtagsViewController: UICollectionViewDelegate { extension TrendingHashtagsViewController: UICollectionViewDragDelegate { func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] { guard let item = dataSource.itemIdentifier(for: indexPath), - case let .tag(hashtag) = item else { + case let .tag(hashtag) = item, + let url = URL(hashtag.url) else { return [] } - let provider = NSItemProvider(object: hashtag.url as NSURL) + let provider = NSItemProvider(object: url as NSURL) if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: mastodonController.accountInfo!.id) { activity.displaysAuxiliaryScene = true provider.registerObject(activity, visibility: .all) diff --git a/Tusker/Screens/Search/SearchViewController.swift b/Tusker/Screens/Search/SearchViewController.swift index 1abc9300..ddc25420 100644 --- a/Tusker/Screens/Search/SearchViewController.swift +++ b/Tusker/Screens/Search/SearchViewController.swift @@ -9,6 +9,7 @@ import UIKit import Pachyderm import SafariServices +import WebURLFoundationExtras class SearchViewController: UIViewController { @@ -295,7 +296,10 @@ extension SearchViewController: UICollectionViewDragDelegate { } switch item { case let .tag(hashtag): - let provider = NSItemProvider(object: hashtag.url as NSURL) + guard let url = URL(hashtag.url) else { + return [] + } + let provider = NSItemProvider(object: url as NSURL) if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: mastodonController.accountInfo!.id) { activity.displaysAuxiliaryScene = true provider.registerObject(activity, visibility: .all) diff --git a/Tusker/Screens/Utilities/Previewing.swift b/Tusker/Screens/Utilities/Previewing.swift index d0059650..c9054b86 100644 --- a/Tusker/Screens/Utilities/Previewing.swift +++ b/Tusker/Screens/Utilities/Previewing.swift @@ -9,6 +9,7 @@ import UIKit import SafariServices import Pachyderm +import WebURLFoundationExtras protocol MenuActionProvider: AnyObject { var navigationDelegate: TuskerNavigationDelegate? { get } @@ -116,7 +117,12 @@ extension MenuActionProvider { actionsSection = [] } - let shareSection = actionsForURL(hashtag.url, sourceView: sourceView) + let shareSection: [UIMenuElement] + if let url = URL(hashtag.url) { + shareSection = actionsForURL(url, sourceView: sourceView) + } else { + shareSection = [] + } return [ UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: shareSection), diff --git a/Tusker/Views/StatusContentTextView.swift b/Tusker/Views/StatusContentTextView.swift index 33657f27..132cbe52 100644 --- a/Tusker/Views/StatusContentTextView.swift +++ b/Tusker/Views/StatusContentTextView.swift @@ -8,6 +8,7 @@ import UIKit import Pachyderm +import WebURLFoundationExtras class StatusContentTextView: ContentTextView { @@ -27,7 +28,7 @@ class StatusContentTextView: ContentTextView { let status = mastodonController.persistentContainer.status(for: statusID) { mention = status.mentions.first { (mention) in // Mastodon and Pleroma include the @ in the text, GNU Social does not - (text.dropFirst() == mention.username || text == mention.username) && url.host == mention.url.host! + (text.dropFirst() == mention.username || text == mention.username) && url.host == mention.url.host!.serialized } } else { mention = nil @@ -41,7 +42,7 @@ class StatusContentTextView: ContentTextView { let mastodonController = mastodonController, let status = mastodonController.persistentContainer.status(for: statusID) { hashtag = status.hashtags.first { (hashtag) in - hashtag.url == url + URL(hashtag.url) == url } } else { hashtag = nil