小粒な Tips の共有をしていきたい: 2023年にメモした iOS アプリ開発 Tips 3選

この記事は はてなエンジニア Advent Calendar 2023 の19日目の記事です。昨日は id:onk さんによる『「キャッシュは麻薬」という標語からの脱却 - id:onk のはてなブログ』でした。普段よく使っている Apollo Client では頻繁に更新するデータに関しては更新のリクエストを投げすぎないようにクライアント側のキャッシュを更新するといった、クライアント側の話題も多くありそうで記事書くと面白そう。

小粒な Tips の共有をしていきたい

技術的な生活において小粒な Tips の共有をしていますか?

同僚とペアプログラミングをしている時に、本人は当たり前だと思っていても周りが知らない Tips やテクニックを使っているケースをたまに見かけます。そういった出会いが自分は好きで、今回は提供する側になれればと思い記事を書きました。タイトルに iOS と書きましたが、macOS などの他の Apple プラットフォームのアプリ開発にも通ずる内容です。

動作環境

> swift --version
swift-driver version: 1.87.3 Apple Swift version 5.9.2 (swiftlang-5.9.2.2.56 clang-1500.1.0.2.5)
Target: arm64-apple-macosx14.0

> xed --version
xed version 15.1

1. extension に @available(*, unavailable) を使って Protocol の準拠を剥がす

Swift の @available は特定の Swift バージョンや 特定のプラットフォーム・特定のOSバージョンに対して利用の制限をかける Attributes です。クラスや構造体だけでなく、extension に対しても使うことができます。

以下は @available(*, unavailable) を使って Identifiable の準拠を剥がしている様子です。Identifiable が要求する id を定義するとコンパイルエラーが起こります。

protocol Animal: Identifiable {}
 
// error: conformance of 'Dog' to 'Identifiable' is unavailable
struct Dog: Animal {
    var name: String
    var id: String { name }
}
 
@available(*, unavailable)
extension Dog: Identifiable {}

これは The Swift Programming Language にも書いてあるテクニックで、Sendable を無効にする例が書かれています。

2. xed は xcodeprojPackage.swift がある場合は後者を優先する

以下のように .xcodeproj と Package.swift がある場合、xed は Package.swift を優先して開きます。

$ls -1 | grep -e ".xcodeproj" -e "Package.swift"
Hoge.xcodeproj
Package.swift

これは apollographql/apollo-ios*1 を手元で触っていた際に知りました。

他の xed で開ける対象の優先順位は以下のとおり。

  1. Package.swift
  2. xcworkspace
  3. xcodeproj
  4. playground

2番目以降は規模感で降順となるため理解できるが、Package.swiftxcworkspace はどちらかというと後者を優先してほしい気がする。

ちなみに、Xcode のメニューバー > File 上では Workspace(xcworkspace) が最下部に置かれています。

Xcode のメニューバー > File > New

3. Deprecated になった関数やプロパティの移行先を知るには Xcode 上で Jump to Definition をすると情報を得やすい

公式ドキュメントには Deprecated になった関数やプロパティの移行先が書かれていないことが多く、「Deprecated になった後は何を使えば良いの?」という疑問を持つことが多々ありました。

例として、Foundation.URL の appendingPathComponent(_:) のドキュメントを見てみます。閲覧日は2023/12/13です。

iOS においては iOS 17.2 までで Deprecated になることは分かりますが、何を代わりに使えば良いかが書かれていません。

そういう時は、Xcode 上で Jump to Definition で定義元を見て @available の message を見ると移行先について書かれていることが多いです。

/// Returns a URL constructed by appending the given path component to self.
///
/// - note: This function performs a file system operation to determine if the path component is a directory. If so, it will append a trailing `/`. If you know in advance that the path component is a directory or not, then use `func appendingPathComponent(_:isDirectory:)`.
/// - parameter pathComponent: The path component to add.
@available(macOS, introduced: 10.10, deprecated: 100000.0, message: "Use appending(path:directoryHint:) instead")
@available(iOS, introduced: 8.0, deprecated: 100000.0, message: "Use appending(path:directoryHint:) instead")
@available(tvOS, introduced: 9.0, deprecated: 100000.0, message: "Use appending(path:directoryHint:) instead")
@available(watchOS, introduced: 2.0, deprecated: 100000.0, message: "Use appending(path:directoryHint:) instead")
public func appendingPathComponent(_ pathComponent: String) -> URL

message に書かれている appending(path:directoryHint:) を使えば良いことが分かりました。

こちらに関してはメソッドの説明すらまだ書かれていない状態ですが、先程と同じように Xcode 上で定義元を見ると、メソッドの説明がドキュメントコメントとして書かれています。

/// Returns a URL constructed by appending the given path to self.
/// - Parameters:
///   - path: The path to add
///   - directoryHint: A hint to whether this URL will point to a directory
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
public func appending<S>(path: S, directoryHint: URL.DirectoryHint = .inferFromPath) -> URL where S : StringProtocol

参考

いかがでしたか?皆さんの Tips ぜひ教えてください。

明日は id:onishi さんです!

*1:apollo-ios は2023/12/13現在 Tuist を採用しており、xcodeproj は Tuist によって生成されるため git の管理下に置かれておらずリポジトリのルートには存在しません。