2014 年 11 月 4 日

Swiftを使ってGoogleMapsを利用する

Swiftを使ってGoogleMapsを利用する

ようやくXcode6に乗り換えて、少しずつSwiftでプログラミングするようになってきました。
Swiftならこのように書くといったサンプルソースコードも多くあるので、改めてiOSのアプリケーション開発を勉強し直す機会にもなっています。

Playgroundを使うと、インタプリタでSwiftの小さなコードを試すことができます。Objective-Cより簡素に書けるので、少し使い出すと誰もがSwiftを使いたいと感じるのではないでしょうか。

前回は、Titanium AlloyでGoogleMapsを使ってみましたが、今回はGoogleMapsを使って地図表示するSwiftプログラムを書いてみました。既に類似のものは存在すると思いますが、以下の点を考慮して書いてみました。

  • Storyboad(xib)は使わない。
  • CocoaPodsでSDKをインストールする。
  • APIキーは、ターゲットのBuild Settingsで設定する。

環境

  1. OSX 10.9.5
  2. Xcode 6.1
  3. CocoaPods 0.34.4
  4. GoogleMaps iOS SDK 1.8.1

GitHubリポジトリ

ソースコードはgithubに置いてあります。

https://github.com/notice/swift-gmaps

CocoaPodsでライブラリダウンロード

ルートディレクトリで、ライブラリをインストールします。しばらくすると、以下のようなログを出力して、./Podsディレクトリが生成されます。

$ pod install
Analyzing dependencies

CocoaPods 0.35.0.rc1 is available.
To update use: `sudo gem install cocoapods --pre`
[!] This is a test version we'd love you to try.

For more information see http://blog.cocoapods.org
and the CHANGELOG for this version http://git.io/BaH8pQ.

Downloading dependencies
Installing Google-Maps-iOS-SDK (1.8.1)
Generating Pods project
Integrating client project

CocoaPods 0.35.0.rc1があるようですね。

後はXcodeで作業します。CocoaPodsを利用するときは、gmaps.xcodeprojではなく、gmaps.xcworkspaceを開きます。

$ open gmaps.xcworkspace

Storyboadを利用しない

Storyboadを利用しない方法は、色々ありますが、私はSingle View Applicationから、Storyboardファイルを削除して、info.plistの”Main storyboard file base name”を空白にしています。Xcode6では、Storyboardファイルを削除するだけでビルドできていますが、念のため。emptyプロジェクトから追加していってもいいのでしょうが、あまりにも何もなさすぎるので、こうしています。

Storyboadを使うべきか、否かは賛否両論というところでしょうか。私は使わないのが好みですが、AppleではStoryboard利用を推奨しているようなので、肩身が狭くなる思いです。

AppDelegate.swiftで初期化

Storyboadを使っていないので、自分でビューコントローラーを初期化します。UIWindowを初期化して、rootViewControllerの設定とviewを追加するだけです。その前にGMSServicesにAPIキーを設定しておきます。ターゲットのbuild settingsの値をInfo.plistで定数化するように設定しているので、Info.plistからAPIキーを取得します。こうしておくと、ビルドのコンフィグレーション(debug,releaseなど)によってAPIキーを切り替えることができます。

参考記事:iOSの環境変数(Configuration, Build Settings, Plist)で知っておくべきこと

サンプルソースコードでは、APIキーを設定せずに実行すると、アプリが異常終了しますが、ご了承ください。また、build settingsを変更した場合、一旦プロジェクトをクリーンしないと正常にビルドできない現象がありましたので、ご注意ください。

  func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Override point for customization after application launch.

    var aDict: NSDictionary?
    if let path = NSBundle.mainBundle().pathForResource("Info", ofType: "plist") {
      aDict = NSDictionary(contentsOfFile: path)
    }
    // GMSServices.provideAPIKey(aDict!.objectForKey("GOOGLE_MAPS_API_KEY") as String)
    if let dict = aDict {
      println("google maps api key = " + (dict.objectForKey("GOOGLE_MAPS_API_KEY") as String))
      GMSServices.provideAPIKey(dict.objectForKey("GOOGLE_MAPS_API_KEY") as String)
    }

    var viewController = ViewController()
    self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
    self.window?.rootViewController = viewController
    self.window?.addSubview(viewController.view)
    self.window?.makeKeyAndVisible()

    return true
  }

コーディングの変数の後ろに?や!というのがあります。Swiftの変数にはOptinal型というものがあり、nil値を許容するものに指定します。C#にはNullable 型というものがありますが、これと同様なものだと理解しています。SwiftのT? は Optional<T> のシンタックスシュガーだそうです。
これとは逆にT!というものがありますが、T!はImplicitlyUnwrappedOptional<T> のシンタックスシュガーとなります。こちらはnil値を許容しません。

if let dict = aDictとして、?を変換してブロック内でdictを使うという書き方をしているのはよく見ます。Optional Bindingと呼ぶらしいですが、書き方の例として使ってみました。

ViewController.swiftでGoogle Map Viewの設定

Swiftでは、ファイル名とクラス名は一致していなくてもいいようです。またヘッダファイルも存在しません。ここでは、UIViewControllerを継承し、GMSMapViewDelegate(このサンプルでは利用していませんが)を実装するクラスを定義します。

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    var target: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 34.689197, longitude: 135.502321)
    var camera: GMSCameraPosition = GMSCameraPosition(target: target, zoom: 12, bearing: 0, viewingAngle: 0)
    var mapView = GMSMapView(
      frame: CGRectMake(0, 0, self.view.bounds.width, self.view.bounds.height)
    )
    mapView.myLocationEnabled = true
    mapView.camera = camera
    mapView.delegate = self

    var marker: GMSMarker = GMSMarker()
    marker.position = CLLocationCoordinate2DMake(34.689197, 135.502321)
    marker.title = "notice"
    marker.snippet = "japan"
    marker.map = mapView

    self.view.addSubview(mapView)

  }

viewDidLoadメソッドをオーバライドしますが、swiftでは明示的にoverrideを宣言します。これはJavaにもある方法ですが、
オーバライドしたメソッドであることが一目でわかりますし、オーバライドしたつもりでもスペルが間違っていたということもなくなります。メンバのアクセス制御にはprivate,internal,publicがあるようですが、今回はまったく考えずに何も指定せず書きました。

GMSMapViewの利用方法は、今まで通りの方法です。AndroidではJavaを使って書いているので、Swiftのコードの方がずっと読みやすいです。ただ、メソッドの引数にラベルをつけるのが、ちょっと違和感がありますが、逆にここはObjective-Cを彷彿させるなと思ってます。

実行スクリーンショット

Objective-Cのライブラリもこのように利用できることは大変重要なことですし、Swiftを使うのが加速しそうです。いままでObjective-Cがなんとなく、苦手だった方もSwiftは好きになるかもしれません。またiOSでのアプリ開発の基本的なところから学び直すチャンスでもあります。ぜひ、一度お試しください。

Xcode6のスプラッシュ画面定義

余談ですが、Xcode6にしてから、LaunchScreen.xibというファイルが自動的に作成されて、これが起動時のスプラッシュ画面として表示されます。デフォルトでは、プロジェクト名とcopyrightが表示されるだけですが、後はこれを修正すれば、よいということなんでしょうね。わざわざ、画像用意しなくてもよくなったのは便利ですね。