FirebaseをPodsを使わず直に取り込む際、DLするVersionに注意
前提
- CocoaPodsを使わない
- Firebaseを直にプロジェクトに取り込む
リンクが古い
前提に該当する人は、下記のリンクからzipファイルをDLする人が大半だと思う。 しかし、リンクを見るとFirebaseのバージョンが3.11となっている
ここで今最新のFirebaseがいくつかRelease Noteを確認すると・・・
このように、細かくサイトはメンテナンスされているわけではないようである。
最新版のURL
最新版を取得するには、ひとまず下記のようにしたらDL可能だった。
https://dl.google.com/firebase/sdk/ios/3_11_0/Firebase-3.11.0.zip?hl=ja
↓
https://dl.google.com/firebase/sdk/ios/3_17_0/Firebase-3.17.0.zip?hl=ja
Swiftの文字列にて、Implicitly Unwrapped Optional型の文字列を扱う際に失敗した話
Implicitly Unwrapped Optional型と呼ばれる下記のような記述を行うことで、 この変数を使う時にUnwrapしてくれるというのは大体の人は知っていると思う。
let hoge: String!
しかし、Unwrapしない時もあり、そのパターンに嵌ったので今後同じことがないように記録
パターン1:文字列に組み込んだ時 “hoge\(fuga)moga”
下記の場合、Unwrapされず、文字列にOptionalとまで入ってしまう。
hogeに対して!
をつけてやればUnwrapされ、欲しい文字列になるだろうが、
Implicitly Unwrapped Optional型なのに、わざわざ!
をつけてやる意味がわからない。
結果、良いか悪いわからないが、下記記述方法に落ち着いた。
また、let hoge: String
と、String
型で定義してやっても、前者の方法でもなんら問題ない
パターン2:ローカル変数に入れた後、メソッドへ渡した時
先にやりたいことを提示しておく。
下記みたく引数にString
型を取るメソッドにString!
型を渡し、Unwrapして使うようにしたい
しかし、一度別の変数に入れた後、その変数をメソッドに渡すとForceUnwrapしなさいと怒られエラーになる
なぜなのか、型を確認したのが、下記となる。
最初String!
型だったのに、変数に入れた際、
String
型でもString!
型でもなく、String?
型になっている
理由
- Swift3からImplicitly Unwrapped Optionalは、Optional型に付属するようになった
- 型評価が必要な時だけUnwrapされる
- それ以外はOptionalとして保持されている www.natashatherobot.com
Frameworkに含まれるXIBやNIBファイルを使用しているのに表示されず"Unknown class HogeClass in Interface Builder file."と出る時の対処方法
3rd Party提供Frameworkに含まれるXIB、NIB
3rd Partyが提供しているFrameworkには、稀にXIBやNIBが含まれており、それを画面上に配置して使うことがある。 この時、Xcodeプロジェクトに取り込み、StoryboardでCustomClassとして適用して使うこともあるだろう。
動作確認すると・・・
配置もバッチリ!動作確認で動かしてみる・・・。 するとどうだろう画面には取り込んだはずのXIBやNIBの面影すらない。
ログを確認すると
Unknown class HogeClass in Interface Builder file.
と出ている。 取り込んだはずなのに、Unknownとかどういうこと?ってなること必死である。
解決方法
Build Setting
>Linking
>Other Linker Flags
に -ObjC
を追加する
Frameworkで忘れがちな-ObjC
これを忘れてはいけない。
追加したら再度実行してみよう。
フリーのApple IDアカウントを使ってXcodeからアプリを実機転送できることで、App ID登録時にエラーを出してしまう可能性の問題
要約
- Xcode7からApple IDアカウントがあれば、有料の開発者登録を行わずとも実機にアプリを転送できるようになった
- その結果App IDを無作為に作成することができ、アカウントが違っても同じApp IDを作成することはできないので、何故かApp IDの登録でエラーになり、ナンデー?!となることも
- フリーアカウントではApp IDの管理が一切できず、有料登録をするのも実際の開発をする人ぐらいなので、App ID取得するだけして放置される問題がある
- 個人的には有料の開発者登録している人のみが実機にアプリを転送できる以前に戻って欲しい
問題の発生内容
1.→ フリーのApple IDアカウントでXcodeにサインイン後、適当なプロジェクトを作成
2.→ Bundle Identifierを任意のものに設定する
3.→ 実機を繋いでビルドする
4.→ 下記スクリーンショットのようにプロビジョニングが自動生成され、App IDも一意のものとして作成される
5.→ 有料登録している開発者アカウントでApple Developerにサインインし、「2.」のBundle Identifierと同じ文字列をApp IDに登録を試みる
6.→ 「There were errors in the data supplied. Please correct and re-submit.」「An App ID with Identifier ‘~’ is not available. Please enter a different string.」
とエラーが出て使えないから別のApp IDにしてねと言われる。
過去に自分が別アカウントとかで作って、忘れてる場合等にはこのエラー内容じゃ何がダメなのかわからないのも問題。
「既に使われています。」くらいのエラーは出して欲しいところ。
対策
- 安直な逆ドメインのApp IDとかは被りやすいため、なるべく複雑なものにする必要がある。
- 個人のフリーアカウントで開発し、リリースは会社等の別アカウントで行う場合、誤ってフリーアカウントでApp IDを取ることもあるため、開発中は仮のBundle Identifierを設定したり、証明書とプロビジョニングをリリース予定アカウントから引っ張ってくる
- ベストなのは、有料登録済みのリリース予定アカウントで、App IDを先に作成しておく
参考URL
iOS(Swift)から3ステップでMastodonに画像付き投稿を行う
まえがき
- 前回は、登録、ログイン、投稿の3ステップを記載
- 前回の延長線上で画像付きの投稿もやってみよと思い立つ
- 画像やmime/typeは固定で記述してるので適宜変更してください。
- 今回もmastdn.jpを使用しています。適宜変更してください。
- タイトルのことが出来る処理の流れを主軸に記述しているため、ForceUnwrap等よくない書き方は意図的にしています。(早めに直します・・・)
今回もQiitaに投稿済み (今後も技術記事は両方に投稿&Wメンテ予定。これに伴い、はてな記法からMarkdownに変更)
準備
extension Data { public mutating func append(_ string: String) { let data = Data(string.utf8) return self.append(data) } }
// 今回はPNG固定でファイル名とmime/typeを使用 let filename = "hoge.png" let mimetype = "image/png" // Create body for media func createBodyWith(parameters: [String: String]?, filePathKey: String?, imageData: Data, boundary: String) -> Data { var body = Data(); if let parameters = parameters { for (key, value) in parameters { body.append("--\(boundary)\r\n") body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n") body.append("\(value)\r\n") } } body.append("--\(boundary)\r\n") body.append("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n") body.append("Content-Type: \(mimetype)\r\n\r\n") body.append(imageData) body.append("\r\n") body.append("--\(boundary)--\r\n") return body } func generateBoundaryString() -> String { return "Boundary-\(NSUUID().uuidString)" }
Step.1 前回の内容を使ってログイン
上記の前記事を参考に、access_tokenを取得します。
Step.2 画像をアップロード
// 画像アップロード先URL let uploadUrl = URL(string: "https://mastdn.jp/api/v1/media")! // params生成 // access_token: Step.1で取得しているもの let params: [String: String] = ["access_token": responseJson["access_token"] as! String] // imageData生成 let imageData = UIImagePNGRepresentation(UIImage(named: filename)!)! // boudary生成 let boundary = generateBoundaryString() // request生成(前回作ったメソッドが使えないため、地道にValue追加) var request = URLRequest(url: urlString!) request.httpMethod = "POST" request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") request.httpBody = createBodyWith(parameters: params, filePathKey: "file", imageData: datas, boundary: boundary) // 画像アップロードPOST let task = session.dataTask(with: request, completionHandler: { data, response, error in do { self.responseJson = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! Dictionary<String, AnyObject> } catch { } }) task.resume()
// responseJsonの中身 { "id": 12356, "preview_url": "https://mastdn.jp/system/media_attachments/files/000/069/064/small/b804bb6a6c8db3d2.png?1492450358", "text_url": "https://mastdn.jp/media/hOgVBgaqylfQM-IolQw", "type": "image", "url": "https://mastdn.jp/system/media_attachments/files/000/069/064/original/b804bb6a6c8db3d2.png?1492450358" }
Step.3 画像ID付きでTootの投稿
// 前回のToot投稿に画像アップロード時のidを付加するだけ // TootURL let tootUrl = URL(string: "https://mastdn.jp/api/v1/statuses")! // access_token: Step.1で取得しているもの // status: Tootの内容 // media_ids: Step.2で取得したid(最大4つまで)。値は配列に格納。 // visibility: 公開範囲。(省略可能) let body: [String: String] = ["access_token": responseJson["access_token"] as! String, "status": "開発アプリからのテスト投稿", "visibility": "public", "media_ids": [responseJson["id"]] as AnyObject] // Toot POST do { try post(url: tootUrl, body: body) { data, response, error in // dataは返ってくるが、投稿できるまでの処理を書くだけなので省略 } } catch { }