読者です 読者をやめる 読者になる 読者になる

Swiftで遊ぼう! on Hatena

あしたさぬきblogでやってた初心者オヤジのiOSプログラミング奮闘記がHatenaに来ました

Swiftで遊ぼう! - 455 - Swift 2 NSURLSessionまだ全然わかっていない...ちょっと分かったかな(ATS)

Swiftで遊ぼう!の古い記事-> Life-LOG OtherSide

  • 2015年10月20日改訂

淀みにハマって動けません。NSURLファイルローディングシステムをどこで勉強していいやら...

SwiftではじめるiPhoneアプリ開発の教科書」なんて本を調べて見ると、Chapter6-2に「UIImageView: Web上の画像を表示する」という章があったので、Xcode7.1β-で試してみる。

// SwiftではじめるiPhoneアプリ開発の教科書の例文 - Xcode6.3板
import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var myImageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        var myURL = NSURL(string: "http://www.ymori.com/itest/test.jpg")
        let myData = NSData(contentsOfURL: myURL)
        let myImage = UIImage(data: myData)
        myImageView.image = myImage
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

これで問題無さそうなのに、let myData = NSData(contentsOfURL: myURL)でエラーです(T_T)

Swift2.0になって随分コーディングが変わりました。エラーハンドリングがSwift 2.0から加わったこともありますが、NSURLクラスの取り扱いも変更されたからです。

NSURLクラスの変更は、セキュリティーに関する変更とエラーハンドリングを組み込まれたメソッドやイニシャライザが用意されたようです。

まず、上記のコードをXcode7.0仕様にするのは意外に簡単です。

// Xcode7.0仕様
import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var myImageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        var myURL = NSURL(string: "http://www.ymori.com/itest/test.jpg")
        let myData = NSData(contentsOfURL: myURL!)
        let myImage = UIImage(data: myData!)
        myImageView.image = myImage
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

じっくり見ないと違いが分からないでしょう。2行目のNSData(contstsOFURL: myURL!)と3行目のUIImage(data: myData!)に強制アンラップ「!」が付いただけです。

このままビルドは問題無くできます。しかし、ランはできないんです。コンソールに次のエラーが表示されます。

f:id:yataiblue:20151021121821j:plain

App Transport Security(ATS)

App Transport Security(ATS)です。これが新しいiOSフレームワークに組み込まれました。NSURLファイルローディングシステムは、デフォルトで「https:」を扱うようになったので、セキュリティ機能の無い「http:」を例外的に「Info.plist」に登録して通さないと駄目になりました。

プロジェクト・ナビゲータにある「Info.plist」をダブルクリックすると編集ができるようになります。最上位の階層で新しいDictionary型ファイル「App Transport Security Settings」を加えます。その下にもう1つファイルを入れます。「Allow Arbitrary Loads」を加えて、「true」にすると今まで通りセキュリティー保護されていないサイト「http:」も自由にアクセスできるようになります。←これは推奨されていません

Dictionary型の「Exception Domains」を加えます。この階層に例外にする特定のドメインをDictionary型として持たせます。上記の場合、「www.ymori.com」をKeyとしてString型を加えます。ValueはDictionary型として設定します。下記の説明が詳しいので勉強します。

[http://dev.classmethod.jp/smartphone/iphone/ios-9-intro-ats/:title=[iOS 9] iOS 9 で追加された App Transport Security の概要] Developers.IO

取りあえず、「NSTemporaryExceptionAllowsInsecureHTTPLoads」を「true」、「NSIncludesSubdomains」を「true」にします。

f:id:yataiblue:20151021160345j:plain

するとこれだけで画像を取ってくることができました!

で、終了じゃありません。

基本的に「コーディング」がSwift 2.0スタイルじゃないということです。私もまだまだですが、Swift 2.0スタイルでコーディングしてみました。コーディングの説明は希望があればします。コメントしてください。

// Swift2スタイル
import UIKit

class ViewController: UIViewController {

 @IBOutlet weak var myImageView: UIImageView!
    
 override func viewDidLoad() {
  super.viewDidLoad()
  guard let myURL = NSURL(string: "http://www.ymori.com/itest/test.jpg") else {
   print("no such a file!")
   return
  }
  let globalQueu = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
  dispatch_async(globalQueu) {
  let myData: NSData
  do { myData = try! NSData(contentsOfURL: myURL, 
            options: NSDataReadingOptions.DataReadingMappedIfSafe)
  } catch {
   print("download is failed")
  }
  let myImage = UIImage(data: myData)
  dispatch_async(dispatch_get_main_queue()) {
   self.myImageView.image = myImage
  }
 }
}

override func didReceiveMemoryWarning() {
  super.didReceiveMemoryWarning()   
  // Dispose of any resources that can be recreated.
 }
}

今日はここまで。