Swift3でSJISを使ったURLエンコードをしたい


Swift3にて、Shift-JISでURLをエンコードする必要が出てきたのですが、  
`String.addingPercentEscapes`が非推奨になっており、代わりに`String.addingPercentEncoding`を使用するように警告が出てしまいました。

いろいろ調べてみたのですが、`String.addingPercentEscapes`を使用せずにShift-
JISでURLエンコードを行う方法が分からなかったため、ご教授いただきたいです。




本来なら、アプリ側をSJISに対応させるのではなく、サーバー側をUTF-8に対応させるのが筋だと思うのですが…。

AppleがSJIS対応のURLエンコード機能をiOSやSwiftの標準ライブラリーに再度付け加える可能性は極めて低いでしょうから、全部自前で処理してやる必要があります。

例えばこんな感じになるでしょう。

[code]

    extension CharacterSet {
        static let rfc3986Unreserved = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~")
    }
    
    extension String.Encoding {
        static let windows31j = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.dosJapanese.rawValue)))
    }
    
    extension String {
        func addingPercentEncoding(withAllowedCharacters characterSet: CharacterSet, using encoding: String.Encoding) -> String {
            let stringData = self.data(using: encoding, allowLossyConversion: true) ?? Data()
            let percentEscaped = stringData.map {byte->String in
                if characterSet.contains(UnicodeScalar(byte)) {
                    return String(UnicodeScalar(byte))
                } else if byte == UInt8(ascii: " ") {
                    return "+"
                } else {
                    return String(format: "%%%02X", byte)
                }
            }.joined()
            return percentEscaped
        }
    
        var sjisPercentEncoded: String {
            return self.addingPercentEncoding(withAllowedCharacters: .rfc3986Unreserved,  using: .windows31j)
        }
    }
    
    let escaped = "今時SJISのシステムなんて…".sjisPercentEncoded
    print(escaped) //->%8D%A1%8E%9ESJIS%82%CC%83V%83X%83e%83%80%82%C8%82%F1%82%C4%81c
    
[/code]

通常SJISと名乗っているシステムは、Windows拡張文字も受け取れることが多いので、上記では`String.Encoding.shiftJIS`ではなく、Windows-31J相当のエンコーディングを使用しています。

実際にこれがデコードできるかどうかは、サーバーサイドが、どうパーセントエンコーディングを処理しているかによりますので、果たして上記のコードが無事に動くかどうかはなんとも言えません。




本来なら、アプリ側をSJISに対応させるのではなく、サーバー側をUTF-8に対応させるのが筋だと思うのですが…。

AppleがSJIS対応のURLエンコード機能をiOSやSwiftの標準ライブラリーに再度付け加える可能性は極めて低いでしょうから、全部自前で処理してやる必要があります。

例えばこんな感じになるでしょう。

[code]

    extension CharacterSet {
        static let rfc3986Unreserved = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~")
    }
    
    extension String.Encoding {
        static let windows31j = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.dosJapanese.rawValue)))
    }
    
    extension String {
        func addingPercentEncoding(withAllowedCharacters characterSet: CharacterSet, using encoding: String.Encoding) -> String {
            let stringData = self.data(using: encoding, allowLossyConversion: true) ?? Data()
            let percentEscaped = stringData.map {byte->String in
                if characterSet.contains(UnicodeScalar(byte)) {
                    return String(UnicodeScalar(byte))
                } else if byte == UInt8(ascii: " ") {
                    return "+"
                } else {
                    return String(format: "%%%02X", byte)
                }
            }.joined()
            return percentEscaped
        }
    
        var sjisPercentEncoded: String {
            return self.addingPercentEncoding(withAllowedCharacters: .rfc3986Unreserved,  using: .windows31j)
        }
    }
    
    let escaped = "今時SJISのシステムなんて…".sjisPercentEncoded
    print(escaped) //->%8D%A1%8E%9ESJIS%82%CC%83V%83X%83e%83%80%82%C8%82%F1%82%C4%81c
    
[/code]

通常SJISと名乗っているシステムは、Windows拡張文字も受け取れることが多いので、上記では`String.Encoding.shiftJIS`ではなく、Windows-31J相当のエンコーディングを使用しています。

実際にこれがデコードできるかどうかは、サーバーサイドが、どうパーセントエンコーディングを処理しているかによりますので、果たして上記のコードが無事に動くかどうかはなんとも言えません。

