import Foundation import Models public struct InstanceSocialClient: Sendable { private let authorization = "Bearer 8a4xx3D7Hzu1aFnf18qlkH8oU0oZ5ulabXxoS2FtQtwOy8G0DGQhr5PjTIjBnYAmFrSBuE2CcASjFocxJBonY8XGbLySB7MXd9ssrwlRHUXTQh3Z578lE1OfUtafvhML" private let listEndpoint = "https://instances.social/api/1.0/instances/list?count=1000&include_closed=false&include_dead=false&min_active_users=500" private let searchEndpoint = "https://instances.social/api/1.0/instances/search" struct Response: Decodable { let instances: [InstanceSocial] } public init() {} public func fetchInstances(keyword: String) async -> [InstanceSocial] { let keyword = keyword.trimmingCharacters(in: .whitespacesAndNewlines) let endpoint = keyword.isEmpty ? listEndpoint : searchEndpoint + "?q=\(keyword)" guard let url = URL(string: endpoint) else { return [] } var request = URLRequest(url: url) request.setValue(authorization, forHTTPHeaderField: "Authorization") guard let (data, _) = try? await URLSession.shared.data(for: request) else { return [] } let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase guard let response = try? decoder.decode(Response.self, from: data) else { return [] } let result = response.instances.sorted(by: keyword) return result } } extension Array where Self.Element == InstanceSocial { fileprivate func sorted(by keyword: String) -> Self { let keyword = keyword.trimmingCharacters(in: .whitespacesAndNewlines) var newArray = self newArray.sort { (lhs: InstanceSocial, rhs: InstanceSocial) in guard let lhsNumber = Int(lhs.users), let rhsNumber = Int(rhs.users) else { return false } return lhsNumber > rhsNumber } newArray.sort { (lhs: InstanceSocial, rhs: InstanceSocial) in guard let lhsNumber = Int(lhs.statuses), let rhsNumber = Int(rhs.statuses) else { return false } return lhsNumber > rhsNumber } if !keyword.isEmpty { newArray.sort { (lhs: InstanceSocial, rhs: InstanceSocial) in if lhs.name.contains(keyword), !rhs.name.contains(keyword) { return true } return false } } return newArray } }