diff --git a/Package.swift b/Package.swift index dfadfe5..94645cb 100644 --- a/Package.swift +++ b/Package.swift @@ -6,5 +6,8 @@ let package = Package( defaultLocalization: "en", platforms: [.iOS(.v16), .macOS(.v13), .tvOS(.v17), .visionOS(.v1), .watchOS(.v9)], products: [.library(name: "LinksKit", targets: ["LinksKit"])], - targets: [.target(name: "LinksKit", resources: [.process("Localizable.xcstrings")])] + targets: [ + .target(name: "LinksKit", resources: [.process("Localizable.xcstrings")]), + .testTarget(name: "LinksKitTests", dependencies: ["LinksKit"]), + ] ) diff --git a/Sources/LinksKit/Model/SocialPlatform.swift b/Sources/LinksKit/Model/SocialPlatform.swift index ffa20e4..96e5096 100644 --- a/Sources/LinksKit/Model/SocialPlatform.swift +++ b/Sources/LinksKit/Model/SocialPlatform.swift @@ -34,9 +34,16 @@ public enum SocialPlatform: Hashable, Sendable { } } + /// Bluesky handles are lowercase domain names. A handle without a dot ("Jeehut", as shared with other platforms) is + /// the short form of an account on the default server and resolves to "jeehut.bsky.social"; a leading "@" is dropped. + static func blueskyHandle(_ handle: String) -> String { + let handle = (handle.hasPrefix("@") ? String(handle.dropFirst()) : handle).lowercased() + return handle.contains(".") ? handle : "\(handle).bsky.social" + } + func url(handle: String) -> URL { switch self { - case .bluesky: URL(string: "https://bsky.app/profile/\(handle)")! + case .bluesky: URL(string: "https://bsky.app/profile/\(Self.blueskyHandle(handle))")! case .facebook: URL(string: "https://facebook.com/\(handle)")! case .github: URL(string: "https://github.com/\(handle)")! case .instagram: URL(string: "https://instagram.com/\(handle)")! diff --git a/Tests/LinksKitTests/SocialPlatformTests.swift b/Tests/LinksKitTests/SocialPlatformTests.swift new file mode 100644 index 0000000..806e433 --- /dev/null +++ b/Tests/LinksKitTests/SocialPlatformTests.swift @@ -0,0 +1,23 @@ +import Foundation +import Testing + +@testable import LinksKit + +@Suite("SocialPlatform") +struct SocialPlatformTests { + @Test(arguments: [ + ("jeehut", "https://bsky.app/profile/jeehut.bsky.social"), + ("Jeehut", "https://bsky.app/profile/jeehut.bsky.social"), + ("@jeehut", "https://bsky.app/profile/jeehut.bsky.social"), + ("jeehut.bsky.social", "https://bsky.app/profile/jeehut.bsky.social"), + ("fline.dev", "https://bsky.app/profile/fline.dev"), + ("@fline.dev", "https://bsky.app/profile/fline.dev"), + ]) + func blueskyLinksResolveShortAndCustomDomainHandles(handle: String, expected: String) { + #expect(SocialPlatform.bluesky.url(handle: handle).absoluteString == expected) + } + + @Test func mastodonLinksUseTheInstance() { + #expect(SocialPlatform.mastodon(instance: "iosdev.space").url(handle: "Jeehut").absoluteString == "https://iosdev.space/@Jeehut") + } +}