mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-10 07:13:28 +01:00
[AltStoreCore] Generalizes Source.sourceID(from:) logic into URL.normalized()
Allows comparing URLs that may have slight (but irrelevant) differences (e.g. trailing slashes).
This commit is contained in:
@@ -373,6 +373,7 @@
|
||||
D5418F172AD740890014ABD6 /* AppScreenshotCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5418F162AD740890014ABD6 /* AppScreenshotCollectionViewCell.swift */; };
|
||||
D54DED1428CBC44B008B27A0 /* ErrorLogTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54DED1328CBC44B008B27A0 /* ErrorLogTableViewCell.swift */; };
|
||||
D552B1D82A042A740066216F /* AppPermissionsCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D552B1D72A042A740066216F /* AppPermissionsCard.swift */; };
|
||||
D552EB062AF453F900A3AB4D /* URL+Normalized.swift in Sources */ = {isa = PBXBuildFile; fileRef = D552EB052AF453F900A3AB4D /* URL+Normalized.swift */; };
|
||||
D55467B82A8D5E2600F4CE90 /* AppShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55467B12A8D5E2600F4CE90 /* AppShortcuts.swift */; };
|
||||
D55467C52A8D72C300F4CE90 /* ActiveAppsWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55467C42A8D72C300F4CE90 /* ActiveAppsWidget.swift */; };
|
||||
D561B2EB28EF5A4F006752E4 /* AltSign-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = D561B2EA28EF5A4F006752E4 /* AltSign-Dynamic */; };
|
||||
@@ -986,6 +987,7 @@
|
||||
D5418F162AD740890014ABD6 /* AppScreenshotCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScreenshotCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
D54DED1328CBC44B008B27A0 /* ErrorLogTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorLogTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D552B1D72A042A740066216F /* AppPermissionsCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPermissionsCard.swift; sourceTree = "<group>"; };
|
||||
D552EB052AF453F900A3AB4D /* URL+Normalized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Normalized.swift"; sourceTree = "<group>"; };
|
||||
D55467B12A8D5E2600F4CE90 /* AppShortcuts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppShortcuts.swift; sourceTree = "<group>"; };
|
||||
D55467C42A8D72C300F4CE90 /* ActiveAppsWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveAppsWidget.swift; sourceTree = "<group>"; };
|
||||
D56915052AD5D75B00A2B747 /* Regex+Permissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Regex+Permissions.swift"; sourceTree = "<group>"; };
|
||||
@@ -1609,6 +1611,7 @@
|
||||
D52A2F962ACB40F700BDF8E3 /* Logger+AltStore.swift */,
|
||||
D56915052AD5D75B00A2B747 /* Regex+Permissions.swift */,
|
||||
D5B6F6A82AD75D01007EED5A /* ProcessInfo+Previews.swift */,
|
||||
D552EB052AF453F900A3AB4D /* URL+Normalized.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@@ -3042,6 +3045,7 @@
|
||||
BF66EECD2501AECA007EE018 /* StoreAppPolicy.swift in Sources */,
|
||||
BF66EEE82501AED0007EE018 /* UserDefaults+AltStore.swift in Sources */,
|
||||
BF340E9A250AD39500A192CB /* ViewApp.intentdefinition in Sources */,
|
||||
D552EB062AF453F900A3AB4D /* URL+Normalized.swift in Sources */,
|
||||
BFAECC522501B0A400528F27 /* CodableError.swift in Sources */,
|
||||
D5F9821D2AB900060045751F /* AppScreenshot.swift in Sources */,
|
||||
BF66EE9E2501AEC1007EE018 /* Fetchable.swift in Sources */,
|
||||
|
||||
60
AltStoreCore/Extensions/URL+Normalized.swift
Normal file
60
AltStoreCore/Extensions/URL+Normalized.swift
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// URL+Normalized.swift
|
||||
// AltStoreCore
|
||||
//
|
||||
// Created by Riley Testut on 11/2/23.
|
||||
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension URL
|
||||
{
|
||||
func normalized() throws -> String
|
||||
{
|
||||
// Based on https://encyclopedia.pub/entry/29841
|
||||
|
||||
guard var components = URLComponents(url: self, resolvingAgainstBaseURL: true) else { throw URLError(.badURL, userInfo: [NSURLErrorKey: self, NSURLErrorFailingURLErrorKey: self]) }
|
||||
|
||||
if components.scheme == nil && components.host == nil
|
||||
{
|
||||
// Special handling for URLs without explicit scheme & incorrectly assumed to have nil host (e.g. "altstore.io/my/path")
|
||||
guard let updatedComponents = URLComponents(string: "https://" + self.absoluteString) else { throw URLError(.cannotFindHost, userInfo: [NSURLErrorKey: self, NSURLErrorFailingURLErrorKey: self]) }
|
||||
components = updatedComponents
|
||||
}
|
||||
|
||||
// 1. Don't use percent encoding
|
||||
guard let host = components.host else { throw URLError(.cannotFindHost, userInfo: [NSURLErrorKey: self, NSURLErrorFailingURLErrorKey: self]) }
|
||||
|
||||
// 2. Ignore scheme
|
||||
var normalizedURL = host
|
||||
|
||||
// 3. Add port (if not default)
|
||||
if let port = components.port, port != 80 && port != 443
|
||||
{
|
||||
normalizedURL += ":" + String(port)
|
||||
}
|
||||
|
||||
// 4. Add path without fragment or query parameters
|
||||
// 5. Remove duplicate slashes
|
||||
let path = components.path.replacingOccurrences(of: "//", with: "/") // Only remove duplicate slashes from path, not entire URL.
|
||||
normalizedURL += path // path has leading `/`
|
||||
|
||||
// 6. Convert to lowercase
|
||||
normalizedURL = normalizedURL.lowercased()
|
||||
|
||||
// 7. Remove trailing `/`
|
||||
if normalizedURL.hasSuffix("/")
|
||||
{
|
||||
normalizedURL.removeLast()
|
||||
}
|
||||
|
||||
// 8. Remove leading "www"
|
||||
if normalizedURL.hasPrefix("www.")
|
||||
{
|
||||
normalizedURL.removeFirst(4)
|
||||
}
|
||||
|
||||
return normalizedURL
|
||||
}
|
||||
}
|
||||
@@ -252,50 +252,8 @@ internal extension Source
|
||||
{
|
||||
class func sourceID(from sourceURL: URL) throws -> String
|
||||
{
|
||||
// Based on https://encyclopedia.pub/entry/29841
|
||||
|
||||
guard var components = URLComponents(url: sourceURL, resolvingAgainstBaseURL: true) else { throw URLError(.badURL, userInfo: [NSURLErrorKey: sourceURL]) }
|
||||
|
||||
if components.scheme == nil && components.host == nil
|
||||
{
|
||||
// Special handling for URLs without explicit scheme & incorrectly assumed to have nil host (e.g. "altstore.io/my/path")
|
||||
guard let updatedComponents = URLComponents(string: "https://" + sourceURL.absoluteString) else { throw URLError(.cannotFindHost, userInfo: [NSURLErrorKey: sourceURL]) }
|
||||
components = updatedComponents
|
||||
}
|
||||
|
||||
// 1. Don't use percent encoding
|
||||
guard let host = components.host else { throw URLError(.cannotFindHost, userInfo: [NSURLErrorKey: sourceURL]) }
|
||||
|
||||
// 2. Ignore scheme
|
||||
var standardizedID = host
|
||||
|
||||
// 3. Add port (if not default)
|
||||
if let port = components.port, port != 80 && port != 443
|
||||
{
|
||||
standardizedID += ":" + String(port)
|
||||
}
|
||||
|
||||
// 4. Add path without fragment or query parameters
|
||||
// 5. Remove duplicate slashes
|
||||
let path = components.path.replacingOccurrences(of: "//", with: "/") // Only remove duplicate slashes from path, not entire URL.
|
||||
standardizedID += path // path has leading `/`
|
||||
|
||||
// 6. Convert to lowercase
|
||||
standardizedID = standardizedID.lowercased()
|
||||
|
||||
// 7. Remove trailing `/`
|
||||
if standardizedID.hasSuffix("/")
|
||||
{
|
||||
standardizedID.removeLast()
|
||||
}
|
||||
|
||||
// 8. Remove leading "www"
|
||||
if standardizedID.hasPrefix("www.")
|
||||
{
|
||||
standardizedID.removeFirst(4)
|
||||
}
|
||||
|
||||
return standardizedID
|
||||
let sourceID = try sourceURL.normalized()
|
||||
return sourceID
|
||||
}
|
||||
|
||||
func setFeaturedApps(_ featuredApps: [StoreApp]?)
|
||||
|
||||
Reference in New Issue
Block a user