Swift 2のカスタムメッセージでエラー/例外をスローする最も簡単な方法は?


136

他の複数の言語で慣れているSwift 2で何かをしたいのですが、カスタムメッセージでランタイム例外をスローします。例(Javaの場合):

throw new RuntimeException("A custom message here")

ErrorTypeプロトコルに準拠した列挙型をスローできることは理解していますが、スローするすべてのタイプのエラーに対して列挙型を定義する必要はありません。理想的には、上の例をできる限り真似できるようにしたいと思います。ErrorTypeプロトコルを実装するカスタムクラスの作成を検討しましたが、そのプロトコルに必要なものを理解することもできませんでした(ドキュメントを参照)。アイデア?


2
Swift 2スロー/キャッチも例外ではありません。
zaph 2015

回答:


194

最も簡単な方法は、定義するためにおそらく1つのカスタムをenumひとつにcaseしているStringそれに接続されています:

enum MyError: ErrorType {
    case runtimeError(String)
}

または、Swift 4以降:

enum MyError: Error {
    case runtimeError(String)
}

使用例は次のようになります。

func someFunction() throws {
    throw MyError.runtimeError("some message")
}
do {
    try someFunction()
} catch MyError.runtimeError(let errorMessage) {
    print(errorMessage)
}

既存のErrorタイプを使用したい場合、最も一般的なタイプはNSErrorであり、カスタムメッセージでタイプを作成してスローするファクトリメソッドを作成できます。


こんにちは、この回答を投稿してから1年になりますが、のString内部を取得できるかどうかを知りたいのですが、可能errorMessageであればどうすればよいですか。
Renan Camaforte

1
@RenanCamaforteすみません、質問がわかりませんか?Stringここに関連しているMyError.RuntimeError(の時に設定throw)、およびあなたはでそれへのアクセスを得るcatch(とlet errorMessage)。
Arkku

1
最も簡単な解決策を求められました。カスタム列挙型、関数などを作成するときの解決策は簡単ではありません。私は、少なくとも一つの方法を知っているが、それはObjective-Cのためのものですので、私はそこにそれを投稿しません
Vyachaslav Gerchicov

3
@VyachaslavGerchicov質問でも指定されているSwiftのより簡単な方法がわからない場合は、Objective-Cを含むより一般的なコンテキストでは単純とは考えていなくても、これが最も簡単な方法です。 。(また、この回答は基本的に列挙型の1行1回の定義であり、関数とその呼び出しは使用例であり、ソリューションの一部ではありません。)
Arkku

1
@Otarはい、しかし…あなたはtry!ここで使われていないについて話しています。確かに、なんらかのがなければ、スローする可能性のある呼び出しを行うこともできませんtry。(また、コードのその部分は使用例であり、実際のソリューションではありません。)
Arkku

136

最も簡単な方法は以下にString準拠させることErrorです:

extension String: Error {}

次に、単に文字列を投げることができます:

throw "Some Error"

文字列自体をlocalizedStringエラーにするには、代わりに拡張できますLocalizedError

extension String: LocalizedError {
    public var errorDescription: String? { return self }
}

これは賢いですが、localizedDescriptionそれ自体を文字列にする方法はありますか?
villapossu

1
とてもエレガントな方法!
Vitaliy Gozhenko 2017

1
確かにエレガント!しかし、テストターゲットでは次のメッセージが表示されます。Redundant conformance of 'String' to protocol 'Error'
Alexander Borisenko 2017

2
どういうわけか、これは私にはうまくいきません。error.localizedDescription文字列をスローした後の解析時に操作を完了できないと言います。
Noah Allen

1
警告:この拡張機能により、外部ライブラリで問題が発生しました。これが私の例です。これは、エラーを管理するサードパーティのライブラリで可能です。StringをErrorに準拠させる拡張機能は避けます。
ブライアンW.ワグナー

20

@ nick-keetsのソリューションは最も洗練されていますが、テストターゲットでは、次のコンパイル時エラーで失敗しました。

Redundant conformance of 'String' to protocol 'Error'

ここに別のアプローチがあります:

struct RuntimeError: Error {
    let message: String

    init(_ message: String) {
        self.message = message
    }

    public var localizedDescription: String {
        return message
    }
}

そして使用するには:

throw RuntimeError("Error message.")

19

このクールなバージョンをチェックしてください。アイデアは、StringプロトコルとErrorTypeプロトコルの両方を実装し、エラーのrawValueを使用することです。

enum UserValidationError: String, Error {
  case noFirstNameProvided = "Please insert your first name."
  case noLastNameProvided = "Please insert your last name."
  case noAgeProvided = "Please insert your age."
  case noEmailProvided = "Please insert your email."
}

使用法:

do {
  try User.define(firstName,
                  lastName: lastName,
                  age: age,
                  email: email,
                  gender: gender,
                  location: location,
                  phone: phone)
}
catch let error as User.UserValidationError {
  print(error.rawValue)
  return
}

あなたはまだ必要として、このアプローチではほとんど利点があるようですas User.UserValidationErrorし、その上に.rawValue。ただし、代わりにCustomStringConvertibleとして実装した場合はvar description: String { return rawValue }、enum構文を使用してカスタムの説明を取得すると、rawValue印刷するすべての場所を経由する必要がないため便利です。
Arkku、

1
ローカライズされたDescriptionメソッドを実装して.rawValueを返すようにする
DanSkeel

16

スウィフト4:

に従って:

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

カスタム例外を定義したくない場合は、次のように標準のNSErrorオブジェクトを使用できます。

import Foundation

do {
  throw NSError(domain: "my error description", code: 42, userInfo: ["ui1":12, "ui2":"val2"] ) 
}
catch let error as NSError {
  print("Caught NSError: \(error.localizedDescription), \(error.domain), \(error.code)")
  let uis = error.userInfo 
  print("\tUser info:")
  for (key,value) in uis {
    print("\t\tkey=\(key), value=\(value)")
  }
}

プリント:

Caught NSError: The operation could not be completed, my error description, 42
    User info:
        key=ui1, value=12
        key=ui2, value=val2

これにより、任意のタイプのカスタム文字列に加えて、数値コードと必要なすべての追加データを含む辞書を提供できます。

注意:これはOS = Linux(Ubuntu 16.04 LTS)でテストされています。


12

追加の拡張機能、列挙型、クラスなどを含まない最も簡単なソリューション:

NSException(name:NSExceptionName(rawValue: "name"), reason:"reason", userInfo:nil).raise()

2
再。私の答えに対するあなたのコメント、これは、定義と列挙または拡張が一度複雑であるといくらか恣意的に決定したという意味でのみ単純です。だから、はい、あなたの答えは「セットアップ」のゼロ行を持っていますが、スローされたすべての例外が覚えるのが難しい複雑でSwiftのような(raise()ではなくthrow)呪文になるという犠牲を払って。ソリューションを、例外がスローされた場所の数と比較throw Foo.Bar("baz")またはthrow "foo"乗算します。IMOは、1行の拡張または列挙型の1回限りの料金がのようなものよりもはるかに好ましいNSExceptionNameです。
Arkku

@Arkkuたとえば、postNotification2〜3個のパラメーターが必要で、そのセレクターはこれに似ています。より少ない入力パラメーターを受け入れることができるように、各プロジェクトでオーバーライドNotificationまたはその両方を行っていますNotificationCenterか?
Vyachaslav Gerchicov

1
いいえ、私も自分の答えでソリューションを使用しません。質問に答えるためだけに投稿しました。それは、私が自分で行うものではないからです。とにかく、それはポイントの外です:私はあなたの答えは私のものやニックキーツのどちらよりも使うのがはるかに複雑であるという意見を支持します。もちろん、考慮すべき他の有効な点があります。たとえば、String準拠するように拡張するのErrorがあまりにも意外である場合や、MyError列挙型が曖昧すぎる場合(個人的には両方に「はい」と答え、代わりにエラーごとに個別の列挙型のケースを実行します。throw ThisTypeOfError.thisParticularCase)。
アーク

6

@Nick keetsの回答に基づいて、より完全な例を次に示します。

extension String: Error {} // Enables you to throw a string

extension String: LocalizedError { // Adds error.localizedDescription to Error instances
    public var errorDescription: String? { return self }
}

func test(color: NSColor) throws{
    if color == .red {
        throw "I don't like red"
    }else if color == .green {
        throw "I'm not into green"
    }else {
        throw "I like all other colors"
    }
}

do {
    try test(color: .green)
} catch let error where error.localizedDescription == "I don't like red"{
    Swift.print ("Error: \(error)") // "I don't like red"
}catch let error {
    Swift.print ("Other cases: Error: \(error.localizedDescription)") // I like all other colors
}

最初に私の迅速なブログに公開されました:http : //eon.codes/blog/2017/09/01/throwing-simple-errors/


1
TBH:私は今やりますthrow NSError(message: "err", code: 0)
eonist

あなた自身の例さえ使わないのですか?:Dああ、そして最初の引数は、そうdomainではないmessage、そうでしょ?
NRitH

1
あなたの権利、ドメイン。そして、いいえ、コードに砂糖を追加しすぎます。私は通常、小さなフレームワークやモジュールをたくさん作り、便利な拡張機能の糖度を低く保つようにしています。最近、私はResultとNSErrorを組み合わせて使用​​しようとしました
eonist

6

エラーをキャッチする必要がなく、すぐにアプリケーションを停止したい場合は、fatalErrorを使用できます。 fatalError ("Custom message here")


3
これはキャッチできるエラーをスローしないことに注意してください。これにより、アプリがクラッシュします。
アディルフセイン

4

@ Alexander-Borisenkoの回答が好きですが、エラーとしてキャッチされたときにローカライズされた説明が返されませんでした。代わりにLocalizedErrorを使用する必要があるようです:

struct RuntimeError: LocalizedError
{
    let message: String

    init(_ message: String)
    {
        self.message = message
    }

    public var errorDescription: String?
    {
        return message
    }
}

詳細については、この回答を参照してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.