2017年9月30日土曜日

シミュレータだとメールが送れない

iOS8、Xcode6当時からずっと直しやがらないバグ。

メールを送信させるために
MFMailComposeViewController *mailPicker = [[MFMailComposeViewController alloc] init];
を書くわけだけど、これがiPhoneシミュレータ上だとnilのままになってしまい、
[self presentViewController:mailPicker animated:YES completion:nil];
で送信用ビューを表示しようとしても落ちる。
iPhone4だと一瞬表示されるけど引っ込んじゃってエラーを吐く。

高い給料と年会費取ってんだからさっさと直せや、ゴルァ!
Appleゥァア゛!
と強く思うけど、実機だと問題ないのでしょうがないけど我慢する。

2017年9月25日月曜日

Xcode 9でArchive時に変なチェックが

Xcode9でアプリを審査提出すべくArchiveしていたら、これまでと手続きが少し変わり、ValidateおよびUploadの段階でこのような同じメッセージが出たので覚書。

App Store distribution options

Include bitcode for iOS content
Allows the App Store to build your app to take advantage of hardware, software or compiler changes.
iOSコンテンツのビットコードを含める
App Storeがあなたのアプリケーションを構築して、ハードウェア、ソフトウェア、またはコンパイラの変更を利用できるようにします。
Upload your app's symbols to receive symbolicated reports from Apple
Crash logs and other diagnostic information from your customers will be symbolicated and viewable within Xcode.
Appleのシンボル化されたレポートを受け取るために、あなたのアプリのシンボルをアップロードしてください。
お客様のクラッシュログやその他の診断情報は、Xcode内で象徴的に表示されます。
最初のチェックのビットコードとは、アプリをコンパイルする時に、最終的に近藤マシン語臣に変換する前段階で、中間コードのようなものに変換するんだそうな。それのことのようで、含めておくといいことあるなんて読んだ。
なんのこっちゃよくわからんけどね。
最初bitcoinって書いてあるのかと思って、驚いた(笑)

次のチェックはおそらくクラッシュ情報なんかをアップルに送信していいかどうかだろう。今までは勝手に送信されてたんじゃないかって思うけど、コンプライアンス上明示し、選択できるようにしたんじゃないかと。

両方ともチェックされたままでいいと思う。

Re-sign "App name"

"App name" needs to be re-signed for App Store distribution.
Select one of the following signing options to continue.
あなたのAppは、App Storeの配布のために再署名する必要があります。
続行するには、次の署名オプションのいずれかを選択します。
App Storeで配布するための署名を自動で管理するか手動でするかの選択。これも上の自動のままでいいだろう。

Distoribution completed with warnings

その後のいつもの手順でUploadは終わったみたいなんだけど、また変なメッセージが出た。
iTunes Storeのオペレーションに失敗したと。
1024x1024のPNGフォーマットのマーケティングアイコンが含まれてないからApp Reviewかベータ App Reviewに提出できないと。
プロジェクトのImages.xcassetsのアイコンのところに1024ptのアイコンを登録する欄が増えとる…orz
他にもiPhoneとiPadでそれぞれ1x〜3xの20ptのアイコンの欄が…。めんどくせえ。
ScreenShot同様に、アイコンもでっかいの一個アップロードして、小さいのはそれを縮小して対応するようにできないのかね? 21世紀なのに…グチグチ…。

アイコン追加してアップロードし直して無事審査提出できたわい。
そのかわりiOS11やiPhone Xへの対応なんかが超テキトーだから、リジェクトされるかもね?(笑)
そんなもんにいちいち対応してられるか! 毎度毎度仕様を変えやがって!

2017年9月23日土曜日

Web関係のViewはどんどん新しく

Webを表示するためのViewは、もともとUIWebViewだったけど、いつの間にかこれdeprecate(使用不可)になってるのな。

かわりに、いつ登場したんだか知らんけど、WKWebView(WebKit View)がオススメになってる。
さらに、SFSafariViewControllerってのもあるようで。

SFSafariViewControllerはXcodeでSafariServices.frameworkをLinkeすると使える。
Xcode9現在、まだStoryboardからは直接扱えないみたいだけど、Safariとほぼ同等の機能が使えるようだ。
デフォルトでタブブラウジングができないものの、cookieとかのデータが共有できるんだと。

カスタマイズして使いたい人はWKWebView、できるだけカスタマイズせずに楽に使いたい人はSFSafariViewみたいな住み分けかしら? 最終的に一本化されるかもしれないけど。

2017年9月21日木曜日

TwitterKit3でツイートできない -> できた

Twitterは公式SDK使用になった

iOS11にアップデートして、自分のアプリからツイッターに投稿しようとしたら、

if SLComposeViewController.isAvailable(forServiceType: SLServiceTypeTwitter) { }
のところで値が false になっちまいまして…orz
むろん今まではうまくいってた。

どうやら、他社のサービスは他社のやり方でやれ、Appleはしらんわ…ということらしい。まあセキュリティの問題とかいろいろあるせいとは思うけど、めんどくさくなるのは勘弁してほしい。
ツイッター公式のTwitter kitというSDKがあって、今後はそっちを使わねばいけなくなるようだ。

詳しくは上のサイトのやり方に従って欲しいが、
  1. ツイッターにサインインしてアプリ情報などを入力
    1. 【重要】PermissionがRead & Writeになってないので、Update Settingsボタンを押してやって直す。(詳しくは後述)
  2. Twitter Kit SDKをダウンロードしてXcodeのプロジェクトにインストール(たいていはLinked Frameworks and Librariesに勝手に入ってくれると思う)
  3. Info.plistに項目追加
  4. ソースコードを編集
という手順になるようだ。

SafariServices.framework、AVFoundation.framework、CoreMedia.framework の3つも必要という情報もあったのだが、少なくともTwitterKit3、Xcode9、iOS11においては不要だった。

TwitterKitをゲット

さっそくツイッターのアプリ登録サイトに情報を入力してみたが…

ツイッターアカウントに電話番号情報が必要

…無情にもはじかれる。
You must add your mobile phone to your Twitter profile before creating an application. 
アプリケーションを作成する前に、携帯電話をTwitterプロファイルに追加する必要がありマース。
自分のツイッターアカウントに、セキュリティ用の携帯電話番号を登録しないといけないようだ。
普通にツイッター使うだけならいらないから無視してたんだけど、とうとうこの日がやってきてしまったか。

携帯電話番号登録のメリットとは!?

  • アカウントのセキュリティ
  • アカウント回復にかかる時間が短縮
  • ショートメールを介してTwitterを使用
  • 友だちがあなたのアカウントを見つけやすくなる
    • やめてくれ! むしろ見つけにくくしてくれ!
ツイッターがハッキングされて番号情報が漏れたら嫌だなあと思いつつ、やむをえずTwitter for iOSから携帯電話番号の登録をした。
登録した携帯にショートメールで認証番号が届くので、それを入力してやったらすぐにさっきのページが通ったよ。
これのConsumer Key (API Key)ってのを、後で自分のプロジェクトのInfo.plistに書き込む。
また、このConsumer Keyと、Keys And Access TokensタブにあるConsumer Secret (API Secret)をプロジェクトのAppDelegate内に書き込むことになる。

TwitterKit ダウンロード

現時点のバージョンは
CocoaPodsというiOSライブラリ管理ツールを使うと、手作業でいちいちダウンロードしてプロジェクトに追加して…ってやらずに済むらしいんだけど、最初にコマンドライン使ったりするようで、俺にとってはかえって敷居が高いので、今回は手作業でやってみる。

このページ中ほどの Install Twitter Kit Manuallyにある
1. Download and unzip Twitter Kit.
からダウンロード。

プロジェクトにインストール

この中の左3つを自分のプロジェクトにインストールしてやる。
右2つはTwitterKit.frameworkに内包されてるエイリアスなんだけど、別物と考えてそのままインストールする。

TARGETの設定


プロジェクトのTARGET > Build Phases > Link Binary With Librariesに
  • SafariServices.framework
  • AVFoundation.framework
  • CoreMedia.framework
の3つを追加するそうだ。
これ、Generalの一番下にある Linked Frameworks and Libraries に反映されるから、そこに追加してやっても同じじゃないのかな?

Info.plistの設定

Info.plistに以下の項目を追加する。
プロパティリストのままでもいいし、右クリックでOpen AsからSource Code表示にしてもいい。
// Info.plist
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>twitterkit-ここに取得したconsumerKeyを書く</string>
    </array>
  </dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>twitter</string>
    <string>twitterauth</string>
</array>

コードを書きませう

AppDelegate

TwitterKitのimportは言わずもがな。
import TwitterKit

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        //TwitterKit初期設定
        Twitter.sharedInstance().start(withConsumerKey: "ここにConsumerKeyを書く", consumerSecret: "ここにConsumer Secretを書く")
        
        return true
    }

//Twitterにログイン時に、認証トークンをディスクにリダイレクト(転送)し保存
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
        
    return Twitter.sharedInstance().application(app, open: url, options: options)
    }

ツイートする処理

    //ツイッターへツイート
    @IBAction func actionBtn(_ sender: AnyObject) {
        //Twitterログイン認証トークンの有無を確認
        if Twitter.sharedInstance().sessionStore.hasLoggedInUsers() {
            //ツイート
            let composer = TWTRComposer()
            composer.setText("イニシャルテキスト") //初期テキスト
            composer.setURL(URL(string: "リンクのURL")) //リンク
            composer.setImage(UIImage(named: "リンクのURL")) //画像
            
            composer.show(from: self) { (result) in
                if result == TWTRComposerResult.done {
                    print("ツイートされた")
                } else {
                    print("ツイートできんかった")
                }
            }
        } else {
            //Twitterログイン処理
            Twitter.sharedInstance().logIn(completion: { (session, error) in
                if session != nil {
                    print("signed in as \(String(describing: session?.userName))")
                } else {
                    print("error: \(String(describing: error?.localizedDescription))")
                }
            })
        }
    }

うまくいかん!

以上でツイートできるはずなのだが、結果は失敗。

今までのSLComposeViewControllerのちっちゃいウィンドウと違い、もっとちゃんと大きなツイートのウィンドウが開き、画像もURLも貼られてるのに、ツイートボタンを押してもツイートされない。

エラーメッセージとしては以下のものが出るが、さっぱりわからん。
CredStore - performQuery - Error copying matching creds.  Error=-25300, query={
    class = inet;
    "m_Limit" = "m_LimitAll";
    ptcl = htps;
    "r_Attributes" = 1;
    sdmn = "https://api.twitter.com";
    srvr = "api.twitter.com";
    sync = syna;
}

Did encounter error sending Tweet: Error Domain=TWTRNetworkingErrorDomain Code=-1011 "Request failed: unauthorized (401)" UserInfo={NSLocalizedFailureReason=, TWTRNetworkingStatusCode=401, NSErrorFailingURLKey=https://api.twitter.com/1.1/statuses/update.json, NSLocalizedDescription=Request failed: unauthorized (401)}

[TwitterKit] Composer did fail: Error Domain=TWTRNetworkingErrorDomain Code=-1011 "Request failed: unauthorized (401)" UserInfo={NSLocalizedFailureReason=, TWTRNetworkingStatusCode=401, NSErrorFailingURLKey=https://api.twitter.com/1.1/statuses/update.json, NSLocalizedDescription=Request failed: unauthorized (401)}

調べると他の人も同じことで困ってる人がいるみたい。ここにも。ここにも。
ツイート内に「^ < > \ | ` または \r」の文字が使われていると401エラーになるそうだが、使ってないしなあ。

Fabricってのが必要?

2014年10月にTwitter社が発表したモバイルアプリ開発プラットフォーム。
安定性、利用者増、収益化、ユーザー認証の側面を網羅した開発キットだそうだ。
どうやらTwitterアプリを開発するにあたってこいつを使ってる人が多いらしいのだが、ツイートするだけとかならいらないのかもしれず、Twitter開発者のサイトでも「Upgrade from Fabric」として少ししか書かれてない様子。
もしどうしても必要なら調べて使うけど…??

うまくいった!

原因はTwitterKitのWebにある管理画面のパーミッションの設定で、表示と実際が違っていたためだ。
Read and Writeが嘘っぱちだった!
デフォルトの設定は表面上Read and Writeになってるんだけど、これが実際はRead onlyになっているものだから、ツイートしようとしても失敗してしまうのだ。
そりゃよっぽど注意しないと気づかねえってば。
責任は完全にTwitter社にある。

アカウント連携時の注意書きを見ると、ツイートできませんって!
それじゃツイートできるわきゃないよ。

直し方

管理画面でPermissionsタブの下の方にあるUpdate Settingsを押してやると、選ばれたとおりのPermissionに直ってくれる。
Fabricとかは全然いらない。
ツイッターゥァア゛ーーー
こんなもので何日間も悩ませやがって!

2017年9月20日水曜日

Swift 4リリース

Xcode 9でさっそくSwift 4がリリースされた。
2.x → 3 ほどの大きな変更はないと思うが、気付いた点があったら追記していく。

@objc推論の廃止

Dependency Analysis Warning
The use of Swift 3 @objc inference in Swift 4 mode is deprecated. Please address deprecated @objc inference warnings, test your code with “Use of deprecated Swift 3 @objc inference” logging enabled, and then disable inference by changing the "Swift 3 @objc Inference" build setting to "Default" for the "アプリ名" target. 
Swift 4モードでのSwift 3 @objc推論の使用は廃止されました。
推奨されない@objcの推論のWarningに対処し、「廃止されたSwift 3 @objc推論の使用」ロギングを有効にしてコードをテストし、 「アプリ名」 targetの 「Swift 3 @objc Inference」ビルド設定を 「Default」にしてください。

——Swift 3で作ったコードを開いたら出た。よくわからんのだけど、Objective-CのクラスとかをSwiftで扱う時に型を推定してくれてたのかな? んで、それが廃止になって、型変換とかを自分で明示的にやれ…とかそんな意味か?
別にObjective-Cのコードとか使ってなかったけど、Xcode 8でデフォルトでそういう設定だったんだろう。それなら自動で直してくれりゃいいのに。グチグチ。
ここをDefaultに変えるとWarningが消える
Build Setting、TARGETSにはアプリ名とアプリ名+Testsの二つがあるが、その両方にSwift 3  @objc inferenceの設定があるので、両方変えてやる必要がある。

#selectorの書き方?

Timer.scheduledTimer(timeInterval: 0.5,
                  target: self,
                selector: #selector(IdleEvents.filterUpdate(timer:)),
                userInfo: nil,
                 repeats: true)

こういうコードをSwift 3で書いてたんだけど、#selectorのところに以下のエラーが。
2番目のエラーにはFixってボタンが出るんでそれを押すと消えるんだけど、一時的なものらしく、また出たり、@objcってのをコード中の変なところに挿入されたり、なんだかよくわからん!
Argument of '#selector' refers to instance method 'filterUpdate(timer:)' that is not exposed to Objective-C
Add '@objc' to expose this instance method to Objective-C

どうやら、#selectorで呼び出す関数、メソッドの頭に「@objc」ということらしい。
@objc func hogehoge() { … }

んで、そんなこんなやってたら自作のクラスにExpected declarationというエラーが出るようになった。
調べたらコードの最後に「@objc」が付けられてた。さっきのFixボタンがいい加減なところに挿入しやがったんだな。

'NSFontAttributeName' has been renamed to 'NSAttributedStringKey.font'

Replace 'NSFontAttributeName' with 'NSAttributedStringKey.font'

'NSForegroundColorAttributeName' has been renamed to 'NSAttributedStringKey.foregroundColor'

Replace 'NSForegroundColorAttributeName' with 'NSAttributedStringKey.foregroundColor'

'NSShadowAttributeName' has been renamed to 'NSAttributedStringKey.shadow'

Replace 'NSShadowAttributeName' with 'NSAttributedStringKey.shadow'

Xcode 9の変更点

2017/9/20にアップデートされたXcode 9について、Mac AppStoreの説明文の訳。

Xcode 9には、iOS11、watchOS 4、macOS High Sierra 10.13用のSwift 4とSDKが含まれています。
  • リファクタリングにより、Swift、Objective-C、C、およびC ++コードの構造を簡単に変更できます。
  • コードエディタは驚くほど速く応答性があり、Markdown構文のネイティブサポートを追加します。
  • Fix-itsはワンクリックでコードを複数改善し、必要なプロトコルメソッドを追加することもできます。
  • 新しいソースコントロールナビゲータと統合されたGitHubアカウントにより、チーム全体のコードを簡単に管理できます。
  • ネットワーク上のデバイス上のiOSおよびtvOSアプリケーションのワイヤレスインストールとデバッグ。
  • シミュレータは実際のデバイスのように見え、動作し、一度に複数のデバイスをシミュレートできます。
  • iOSのPlaygroundテンプレートは、XcodeとiPadのSwift Playgroundの両方で動作するドキュメントを作成します。
  • Find navigatorは非常に高速で、結果はすぐに表示されます。
  • プロジェクトナビゲータは自動的にファイルとグループをFinderとソースコントロールと同期させます。
  • XcodeサーバーはもはやmacOSサーバーを必要とせず、Xcode環境設定で完全に設定することができます。
  • 次世代のビルドシステムは、多くのプロジェクトを構築するとき(オプションで有効)、信頼性とパフォーマンスを向上させます。
  • Swift 4コンパイラは、Swift 3コードを構築して、一度に1つのモジュールで段階的な移行を可能にします。
  • 最新のSDKには、機械学習のためのCore ML frameworkと、拡張現実のためのARKitが含まれています。
えーと、Swift 4がもうリリースされたのだね Σ(((°Д°;))))ガクガク

Photo Libraryへのアクセスについて

フォトライブラリの画像を読み書きするとき、これまではInfo.plistの「Privacy - Photo Library Usage Description」にだけ「フォトライブラリにアクセスします」とかのメッセージを設定すれば良かったけど、「Privacy - Photo Library Additions Usage Description」にも「フォトライブラリに画像を追加します」とかのメッセージの設定が必要になったようだ。
設定しておかないとアプリが落ちるので困る。

2017年9月19日火曜日

URLスキーム/URLで地図アプリを開く

やりたいこと

緯度経度の位置情報を持ったリンク(たとえばTwitterのツイートとか)を押すと、自動的に地図アプリを起動して指定した場所を表示してくれるようにしたい。

こういうのをURLスキームという。

2種類のスキーム

Appleのマップ用

iOSの場合のURLスキームは以下のものとなる。
http://maps.apple.com/maps?パラメータ

これはApple純正マップ.appがインストールされているデバイスではマップ.appが優先的に起動し、非インストール機(WindowsやAndroid含む)ではGoogleMapが起動される(GoogleMapアプリがなければブラウザ上で)。

GoogleMaps専用

詳しくは公式ガイドページ
マップ.appがインストールされたデバイスでもGoogleMapを起動することもできる。
https://maps.google.com/?パラメータ
https://www.google.com/maps/preview/パラメータ

こちらはMacなら直接ブラウザで、iOSのTwitterアプリならそれの子プロセスとしてWebビューが開く。
iOSの場合、右上のアイコンからSafariで開き、さらにSafariで開いた画面上部に"Google Maps" Appで開くという表示から、GoogleMapsアプリで開くこともできる(アプリがインストールされていればだが)。
試してないがWindowsでもAndroidでも同じような感じだろう。
GoogleMapsアプリで開くまでが少し手間だが、難しいことではないし、たいていは子プロセスとしてのWebビューで事足りるだろう。

comgooglemaps://?パラメータ
などという方法もあるが、こちらは直接ブラウザを経由せずにGoogleMapアプリを開くためのもので、ツイートに直接書き込んでもURLとして認識されない。
ブラウザのURL欄に書いた場合、iOSでは直接GoogleMapsアプリが開けたが、アプリがないMacだと当然ダメ。素直にhttps〜形式の方がいい。

どっちの方がいいか

機能としてはGoogleMapsの方がStreetViewがあったりと高機能なので、当面は後者のURLスキームの方が良さそうだ。
同じ緯度経度、表示範囲(拡大率)を維持したまま相互を切り替えられるといいんだけどな。