🐹

SwiftのURL型を正しく理解しよう!

に公開

概要

これまでなんとなくでURL型を使っていたので、URL型を操作する上で、基礎的な知識や基本操作をまとめました。
本記事では、SwiftでのURLの基本構造から、パスの操作、エンコード、ディレクトリ管理、最新のAPI仕様などをカバーしてます。
URLを正しく安全に使えるようになるために、是非ご活用ください。

URLとは

URL(Uniform Resource Locator)は、Web上のリソース(Webページ、API、ファイルなど)の アドレスを指定するための文字列です。
URLは次のような構造を持っています。

scheme://host/path?query#fragment

要素 説明
スキーム (Scheme) 通信プロトコルを指定 https, http, file
ユーザー情報 (User Info)(省略可) 認証用のユーザー名とパスワード user:pass@
ホスト (Host)(省略可) サーバーのドメイン名または IP アドレス example.com, 192.168.1.1
ポート (Port)(省略可) サーバーのポート番号 :80, :443, :21
パス (Path) サーバー内のリソースの場所 /index.html, /api/users
クエリ (Query)(省略可) 追加のパラメータ ?search=swift&page=1
フラグメント (Fragment)(省略可) ページ内の特定の場所を指定 #section1

絶対URLと相対URL

SwiftのURL型には、絶対URLと相対URLそれぞれに対応したイニシャライザーやメソッドが用意されています
したがって、両者の違いを理解することが重要です。
では、絶対URLと相対URLの違いを確認した上で、対応するイニシャライザーなどを見ていきましょう。

絶対URLは、スキーム(scheme)、ホスト(host)、パス(path) など、アクセス先を特定するのに必要なすべての情報を含んだ完全なURLです。

相対URLは、基準となるURL(baseURL)に対する部分的なパスです。
スキームやホストが含まれていないため、単独ではアクセスできず、基準 URLと組み合わせることで有効なURLになります。

init(string:)

絶対URLを初期化する際に使用します。

let url = URL(string: "https://www.apple.com")!
print(url) // https://www.apple.com

init(string:encodingInvalidCharacters:)

encodingInvalidCharactersがtrueの場合、パスやクエリ部分の特殊文字が自動でエンコードされます。
falseにすると、特殊文字を含むURLはnilになります。

let url = URL(string: "https://zenn.dev/search?q=sw ift", encodingInvalidCharacters: true)!
print(url) // https://zenn.dev/search?q=sw%20ift

let fileURL = URL(string: "file:///Applications/fol der1", encodingInvalidCharacters: true)!
print(fileURL) // file:///Applications/fol%20der1

init(string:relativeTo:)

相対URLを基準URL(baseURL)に対して結合し、絶対URLを生成します。
baseURLを含むURLを出力すると、相対パス-基準URLの形式で出力されます。

let baseURL = URL.applicationDirectory
let fileURL = URL(string: "folder1", relativeTo: baseURL)!

print(fileURL) // folder1 -- file:///Applications/
print(fileURL.absoluteString) // file:///Applications/folder1

absoluteString

絶対URLをString で取得します。

let baseURL = URL.applicationDirectory
let fileURL = URL(string: "folder1", relativeTo: baseURL)!

// 絶対URL
print(baseURL.absoluteString) // file:///Applications/
// 相対URLを含むURL
print(fileURL.absoluteString) // file:///Applications/folder1

relativeString

baseURLを基準にした相対パスを取得します。
baseURLがない場合や、絶対URLに対して使用するとabsoluteStringと同じ挙動になります。

// 相対URLを含むURL
let baseURL = URL.applicationDirectory
let fileURL = URL(string: "folder1", relativeTo: baseURL)!
print(fileURL.relativeString) // folder1

// 絶対URL
let url = URL(string:"https://zenn.dev/search?q=swift")!
print(url.relativeString) // https://zenn.dev/search?q=swift

ディレクトリの取得

Swiftではアプリが使用するディレクトリをURL型のstaticプロパティを使って取得できます。
用途に応じて適切なディレクトリを選択することが重要です。

applicationDirectory

macOSの/Applications/ディレクトリを指す。
このディレクトリには通常、ユーザーがインストールしたアプリケーションが保存されます。

print(URL.applicationDirectory)
// file:///Applications/

documentsDirectory

iOS / macOS アプリがユーザーデータを保存するためのディレクトリ。
アプリがユーザーごとにデータを管理する際に使用される。

print(URL.documentsDirectory) 
// file:///Users/xxxx/Library/Developer/XCPGDevices/0FFCAE8F-7803-4943-A3C6-F6C3EC197D00/data/Containers/Data/Application/238BC63A-F053-4E7F-9A1B-CA38A9F206B3/Documents/

// xxxxはユーザー名

他にも様々なディレクトリが取得できるので、気になる方はご確認ください。

https://developer.apple.com/documentation/foundation/url/3988451-applicationdirectory

URLの構成要素を取り出すメソッド

ここでは、冒頭で触れたURLの要素を取得するためのメソッドを見ていきます。
なので、冒頭の内容を思い出しながらお読みください。
SwiftではURL型の各構成要素を取得するためのメソッドが提供されています。
ただし、hostpathqueryfragment などのプロパティはiOS 18.4 / macOS 15.4 などのバージョンで非推奨(deprecated)となっているため、対応するメソッドを使用する必要があります

host(percentEncoded:)

URLのホスト(ドメイン名やIPアドレス)を取得する。
ホストが存在しない場合はnilを返します。


let url = URL(string: "https://zenn.dev/search?q=swift")!
print(url.host() ?? "nil") // zenn.dev

let url = URL.applicationDirectory.appending(path: "folder1")
print(url.host()) // nil

path(percentEncoded:)

URLのパス部分を取得する。
URLにおいてpathは常に存在するものとみなされる ため、pathがない場合は ""(空文字)として扱われます。
percentEncodedはデフォルトtrueにで、空白などが置き換えられる。

let url = URL(string:"https://zenn.dev/search?q=swift")!
print(url.path()) // /search

// パスがない時
let url = URL(string:"https://zenn.dev")!
print(url.path()) // 何もprintされない(空文字)

// 特殊文字あり(全角スペース)
let baseURL = URL.applicationDirectory.appending(path: "fold er1")
print(baseURL) // file:///Applications/fold%E3%80%80er1
print(baseURL.path()) // /Applications/fold%E3%80%80er1

URLの構成要素を取り出すプロパティ

一部の要素(scheme/ port/ lastPathComponent)は、プロパティとして直接取得可能です。
ここでは、よく使うlastPathComponentのみ紹介します。

lastPathComponent

URLの最後のパスコンポーネント(ファイル名やフォルダ名)を取得する。
パスが存在しない場合は 空文字 ("") を返します。

let url = URL.applicationDirectory.appending(path: "folder1/sample1.txt")

print(url) // file:///Applications/folder1/sample1.txt
print(url.lastPathComponent) // sample1.txt

  
// パスがない時
let url = URL(string: "https://zenn.dev")!
print(url.lastPathComponent) // 何もprintされない(空文字を返す)

URLの結合と削除

URLの結合と削除のメソッドは、下記のような命名の違いを理解しておくとわかりやすいと思います。
ここでは、appendingdeletingのみ紹介します。

  • append/deleteは戻り値なし
  • appending/deletingは新しいURLを返す

appending(component:directoryHint:)

既存のURLにコンポーネントを追加する。
ファイルを追加する場合は.notDirectory、フォルダの場合は.isDirectoryを指定する。
directoryHintを省略すると自動で推論される。
特殊文字が含まれた場合は、encodeされる。

let baseURL = URL.applicationDirectory.appending(path: "folder1")
let newFilePath = baseURL.appending(component: "sample1.txt", directoryHint: .notDirectory)
print(newFilePath) // file:///Applications/folder1/sample1.txt

// 特殊文字(半角スペース)
let baseUrl = URL.applicationDirectory.appending(path: "folder1")
let newFilePath = baseUrl.appending(component: "sam ple1.txt", directoryHint: .notDirectory)

print(newFilePath) // file:///Applications/folder1/sam%20ple1.txt

appending(path:directoryHint:)

パスを利用してURLに追加する。
基本的にappending(component:)と同じだが、/ を含むパスを渡せる点が異なる。
特殊文字が含まれた場合は、path(percentEncoded:)と同様にencodeされる。

let url = URL.applicationDirectory.appending(path: "folder1/sample1.txt")
print(url) // file:///Applications/folder1/sample1.txt

// 特殊文字ありの時(半角スペース)
let url = URL.applicationDirectory.appending(path: "fold er1")
print(url) // file:///Applications/fold%20er1

appending(components:directoryHint:)

複数のパスコンポーネントを一括で追加する。

// ディレクトリ
let url = URL.applicationDirectory
print(url.appending(components: "folder1", "folder2", "folder3", directoryHint: .isDirectory)) // file:///Applications/folder1/folder2/folder3/

// ディレクトリとファイル
let url = URL.applicationDirectory
print(url.appending(components: "folder1", "folder2", "file.txt", directoryHint: .notDirectory)) // file:///Applications/folder1/folder2/file.txt

appendingPathComponent(_:confirmingTo:)

特定のファイル形式(UTI)に準拠したパスを追加する。
⚠️引数がconfirmingTo以外の同一名のメソッドは、deprecatedになっているので注意

let url = URL.applicationDirectory
print(url.appendingPathComponent("sample1", conformingTo: .plainText)) // file:///Applications/sample1.txt

appendPathExtension(for:)

指定したユニフォームタイプ識別子(UTI)に適した拡張子を追加する。

let url = URL.applicationDirectory
let filePath = url.appending(component: "sample1").appendingPathExtension(for: .plainText)

print(filePath) // file:///Applications/sample1.txt

deletingPathExtension

URLの拡張子を削除する。
⚠️ パスがない場合は何も変更されない

let url = URL.applicationDirectory.appending(path: "folder1/sample1.txt")
print(url.deletingPathExtension()) // file:///Applications/folder1/sample1

// 🕵️🕵️🕵️🕵️🕵️🕵️🕵️
// パスが存在しない場合は何も変更しない
let baseURL = URL(string: "http://www.example.com")!
print(baseURL.deletingLastPathComponent()) // http://www.example.com

deletingLastPathComponent

URLの最後のパスコンポーネント(ファイルやフォルダ)を削除する。
⚠️ パスがない場合は何も変更されない

let url = URL.applicationDirectory.appending(path: "folder1/sample1.txt")
print(url.deletingLastPathComponent()) // file:///Applications/folder1/

// 🕵️🕵️🕵️🕵️🕵️🕵️🕵️
// パスが存在しない場合は何も変更しない
let baseURL = URL(string: "http://www.example.com")!
print(baseURL.deletingLastPathComponent()) // http://www.example.com

参考記事

https://developer.apple.com/documentation/foundation/url

Discussion