- 投稿日:2020-03-15T23:22:35+09:00
シワ(Sign in with Apple) 対応
Sign in with Appleとは
Apple ID を利用したサインイン方法。
サードパーティーログインを使用しているアプリは2020/04までにSign in with Appleに対応する様に義務化した。App Storeに提出されるすべての新しいAppやアップデートは、2020年4月30日までにこれらのガイドラインに従う必要があります。
引用元:https://developer.apple.com/jp/news/?id=03042020dそうです。いつものAppleの無茶振りです。
対応しないとリジェクトされてしまう為、仕方なく実装する事に、、
まずは仕様からアプリへの公開情報
アプリに公開される情報は大きく分けて以下の2つです。
- 名前
- メールアドレス
- 公開メールアドレス
- プライベートメールアドレス
これらの値はSign in with Apple新規登録時かユーザーがアプリと紐付け解除を行った後の初回のみ設定する事が出来ます。
名前
アプリに連携する名前はアプリ毎にユーザーが設定することができます。
デフォルトではiCloud アカウントに設定している姓名が入ってきますが、公開したくない場合は任意の名前に変更可能になっています。公開メールアドレス
ユーザーがiCloudの登録に使用しているメールアドレス。
* 実際のメールアドレスなので転送を停止する機能などは今回対象外プライベートメールアドレス
認証時にユーザーがメールアドレス(Apple ID)を非公開設定すると、Appleが自動生成したランダムな英数字のメールアドレスをアプリ(サービス)に連携する。その結果ユーザーは自身のメールアドレスを公開する事なくアプリでログインが可能になる。
* アプリとのメールやりとりはAppleが アプリ→自動生成メールアドレス→プライベートメール という形でメールを転送してくれる。(設定の必要あり)
* ユーザーはメールの転送を停止したり、転送先のメールアドレスを変更する事が出来る。このプライベートメールアドレスは出来るとユーザーにとって何が嬉しいのか?
サービス登録時に実際のメールアドレスを使用するとサービスの利用を停止した後にもメールアドレスを保持されて悪用されるリスクがある。そこでこのプライベートメールアドレスを使用すると実際のメールアドレスはサービスに公開せず、利用停止したい場合には紐付け解除(プライベートメールアドレスの破棄)を行えば悪用されなくなるというメリットがある。アプリとの紐付けの解除
iCloudの管理画面からアプリとの紐付け解除を行うことが出来る。
ユーザーにとって※iOS13以上では[設定>Apple ID(一番上の箇所)>パスワードとセキュリティ>Apple IDを使用中のApp]からでも解除出来る
CSRF対策
Sign in with AppleはCSRF対策の為にAppleの認証APIを実行する際にstateを指定することができる。
簡単に言うとなりすまし防止の為にアプリで送信時に作成したstateとAppleの認証時に返されたstateが一致しているかを確認する。完璧に仕様が分かったことで対応する事を以下に記載。
対応内容
あくまでアプリが対応する内容を記載する。
- コーディング
- iOS13以上の対応
- iOS12未満の対応(必須ではない)
- Android対応(必須ではない)
- Apple Developerでの設定
- Sign in with Appleの有効化
- メールのホワイトリスト追加
- ServiceIDの登録
- Xcodeの設定
- Sign in with Apple有効化
- デザイン対応
- ガイドラインに沿ったデザイン
iOS13での対応
Appleのログインボタンを生成する。
LoginViewController.swiftfunc setupProviderLoginView() { let authorizationButton = ASAuthorizationAppleIDButton() authorizationButton.addTarget(self, action: #selector(handleAuthorizationAppleIDButtonPress), for: .touchUpInside) self.loginProviderStackView.addArrangedSubview(authorizationButton) }ユーザーの姓名とメールアドレスを受け取る様な認証のリクエストをAppleに対して行う。
LoginViewController.swift@objc func handleAuthorizationAppleIDButtonPress() { let appleIDProvider = ASAuthorizationAppleIDProvider() let request = appleIDProvider.createRequest() request.requestedScopes = [.fullName, .email] let authorizationController = ASAuthorizationController(authorizationRequests: [request]) authorizationController.delegate = self authorizationController.presentationContextProvider = self authorizationController.performRequests() }Delegateで結果を受信する。
LoginViewController.swiftextension LoginViewController: ASAuthorizationControllerDelegate { func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential { // 成功した場合 let userIdentifier = appleIDCredential.user let fullName = appleIDCredential.fullName let email = appleIDCredential.email } } func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { // エラーが発生した場合 } }iOS12以下での対応
WebViewを利用してApple JSに対応する必要があります。
サーバサイドの実装が必要だったりAppleボタンを自作しなければならなかったりするので
出来ればiOS13以降のみの対応の方が無難だと思います。デザイン対応
Appleのガイドラインの記載を抜粋。
* Appleのボタンを目立つようにする
* 他サードパーティのログインボタンよりも小さくしないこと
* スクロールしないでもAppleのボタンが表示されるようにする
* ボタンの色は背景色と被らないようにする
などなどガイドラインに従わないとリジェクトされます。
実際に初めてSign in with Apple対応した時にはボタンと背景の色やフォントサイズなどでリジェクトされました。
(iOS12対応の為にボタンは自作しました)https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/
まとめ
iOS12以下に対応する為には、自作したボタンをAppleのデザインに合わせたり認証をWebViewで行ったりしないとならなく別対応が必要なので骨が折れますが、
iOS13のみなら基本Appleが提供しているSDKを使用すれば比較的簡単に開発が済みます。
最近(2020/03時点)リジェクトが厳しくなっている気がするのでデザインのガイドラインなどはしっかり読んで対応した方が良いです。参考URL
- 投稿日:2020-03-15T23:21:23+09:00
ライブラリ作ったから見てくれ
申し訳ありません。「見て下さい。」ですね。
調子乗りました。気を取り直して
こんにちはこんばんは。初投稿のいるべです。
昨年クリスマスに独り身でしにたくなったのでswiftの勉強を始めました。
情報系の学部にいながらプログラミングとは関わらない生活をしていたのでプログラミング歴は実質3ヶ月程となります。今、自分のお気に入りのものを教えてよって主旨のアプリを作成してます。SNSは素人には無理ゲーとか言う声が聞こえてきそうですがまぁやるだけタダなので黙ってて下さい。
今回は表題の通り。そのアプリを作る上で欲しくなった機能をライブラリとして作成したので、「まぁ見てって下さいよ」というところでつらつらと書かせて下さい。
ただの宣伝にならないように色々と書けたらいいなと思います。まず前提。なんでこんなものを作ったか、だ。
いやいや、その「こんなもの」を先に言えよと思う方もいらっしゃるかも知れんがスルーします。
UICollectionViewって意味分かんなくね?
玄人の皆様方に置かれましては何が?と思われるかも知れないが、
- まずdelegateが意味わからん。
- flowlayout何それ??
- そもそもRunさせても何も表示されん、、
その他壁は沢山あると言っても過言では無い。(断言)
少なからず全く調べずにcollectionViewを使って意図した挙動を実装することができる人はそう多く無いのではと考えている(え?みんなふつーに書けるの、、?)みんな悩んでるやろし作ったろ!
まぁもちろん既に上位互換ライブラリは存在していますが経験です。沢山勉強になりました。
僕と同じような初心者の皆様方も、「既にあるからそれ使おう」「既にあるから作るのやめよう」と言うのは悪くも無いけど良くも無いことがあるということを覚えておきましょう。たまには遠回りも必要ということです。お待たせたしました。
今回作ったものはこちらになります。
と画像をのっけてもインパクトがない。。
真ん中下のネコを妹に描いてもらった。ほんの1分程度でさらっと。すごい。タグを簡単に生成したいやんってことでCheckableTagというライブラリを作成しました。
簡単に説明しますと、簡単にタグを作成できるライブラリです。
更に、タップしたら色が変わるようになっているのでチェックボックスのような使い方もできます。
画像を見ての通り、タグの形にもいくつかあるので選ぶのに悩んじゃいますね?(☝︎ ՞ਊ ՞)☝︎導入
現在はPodのみの対応です。
pod 'CheckableTag'
を追加してpod installして下さい。使い方
import UIKit import CheckableTag class ViewController: UIViewController { let items = ["Hello"] let tagView: CheckableTag = { let view = CheckableTag() return view }() override func viewDidLoad() { super.viewDidLoad() tagView.frame = self.view.bounds self.view.addSubview(tagView) tagView.dataSource = self } } extension ViewController: CheckableTagDataSource { func getSelected(sender: CheckableTag) -> [Bool]? { return nil } func getItems(sender: CheckableTag) -> [String] { return items } }最低限のコードはこれだけです。
これで、下のような感じになります。*トリミングしてあります。
タップすると、
色が変わります。プロパティ
まぁここは興味があれば自由に弄ってみて下さい。
いるべ的には下記の設定が好きです。//tagの形を変更 tagView.cellType = .curve //tagの表示スタイルを変更 tagView.cellStyle = .groove //ユーザが選択可能かどうか指定 tagView.canSelected = false //フォントサイズの指定。fontsizeによってtagのサイズが変わる。 tagView.fontSize = 20色の変更
/// 選択状態の色を指定する /// - Parameters: /// - text: テキストカラー /// - back: バックグラウンドカラー /// - line: 枠線カラー tagView.setSelectedColor(text: .white, back: .red, line: .black) /// 非選択状態の色を指定する /// - Parameters: /// - text: テキストカラー /// - back: バックグラウンドカラー /// - line: 枠線カラー tagView.setUnSelectedColor(text: .gray, back: .white, line: .gray)選択状態の取得
//存在する全てのタグの選択状態を取得します。 //trueのとき選択されている //falseのとき非選択状態。 let isSelectedArray: [Bool] = tagView.getIsSelected()細かいことを指定する場合
DataSourceの使用例
tagView.dataSource = self ------------------------------ extension ViewController: CheckableTagDataSource { ///初期選択状態を指定できる。 ///canSelect = falseにしてこれと組み合わせればタグになる。 func getSelected(sender: CheckableTag) -> [Bool]? { return [true, false, true, false, true, true] } ///tagを生成するためにタグのテキストを伝えられる。 func getItems(sender: CheckableTag) -> [String] { return items } }Delegateの使用例
tagView.delegate = self ---------------------------- extension ViewController: CheckableTagDelegate { ///セルを選択したときの動作を設定できる。 func didSelected(cell: CheckableCellProtocol) { print(cell.textLabel.text) } }Animationの使用例
tagView.animation = self ---------------------------- ///このアニメーションは意味不明なのでただの参考として扱って下さい。 extension ViewController: TouchCellAnimationProtocol { ///タップし始めのときに行われるアニメーション。 func touchStartAnimation(cell: CheckableCellProtocol) { UIView.transition(with: cell, duration: 1.0, options: [.transitionFlipFromTop], animations: nil, completion: nil) } ///タップ終わりのときに行われるアニメーション。 func touchEndAnimation(cell: CheckableCellProtocol) { UIView.transition(with: cell, duration: 1.0, options: [.transitionFlipFromBottom], animations: nil, completion: nil) } }選択状態について
collectionViewやtableViewの選択状態を管理する時にスクロールしたら選択状態がずれていることがあります。
これはセルごとで選択状態を管理するのではなく、セル全ての選択状態を管理する配列を用意してそれを読んであげればそんな問題とはおさらばです。///collectionviewを使っている上位view //cellに代入するテキストの配列。 private(set) var items: [String] = [] //cellの選択状態を管理する配列 public lazy var isSelectedItems: [Bool] = { return [Bool].init(repeating: false, count: items.count) }()///cellが生成されるタイミング /// UICollectionViewDataSourceを継承 public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { //cellを再利用するために引っ張り出してくる let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) //選択状態を管理している配列から、今扱っているcellに該当する選択状態を見て、それに見合ったスタイルを反映する。 if isSelectedItems[index.row] { cell.selectedColor(text: textColor, back: backColor, line: lineColor) } else { cell.unSelectedColor(text: textColor, back: backColor, line: lineColor) } return cell }Enumは便利。
Enumってすごいですね。僕は気持ちが先走ってよくタイポをしてしまう人間なのですが,
予測変換で出てこればタイポもないし、後からの要素の追加も楽々!今回はEnumと後述するProtocolに救われました。public enum CellType: String { case square = "SquareCheckableCell" case curve = "CurveCheckableCell" case round = "RoundCheckableCell" case circle = "CircleCheckableCell" }これは実際のコードです。Stringがうだうだしてますね。
タイポしやすい文字列も
cellType.rawValue
を使えばここで設定している文字列が取得できますからタイポが一気に減ります。
最初は
case square
だけだったんですが、一度一通り作って仕舞えばもう簡単!
case *** と新しい要素をここに追加してビルドします(おい)
もちろんビルドは通りません。そのビルドに対して出たエラーのところを修正すればあら不思議。
もうまた別の要素が使えるようになっているではないですか。///指定されたcellのタイプを返す。 public func getCellType(collection: UICollectionView, index: IndexPath) -> CheckableCellProtocol { switch cellType { case .square: return collection.dequeueReusableCell(withReuseIdentifier: cellType.rawValue, for: index) as! SquareCheckableCell case .curve: return collection.dequeueReusableCell(withReuseIdentifier: cellType.rawValue, for: index) as! CurveCheckableCell case .round: return collection.dequeueReusableCell(withReuseIdentifier: cellType.rawValue, for: index) as! RoundCheckableCell case .circle: return collection.dequeueReusableCell(withReuseIdentifier: cellType.rawValue, for: index) as! CircleCheckableCell } }今回はご覧の通り似てるけど違うものをいくつも作ったのでstrategyパターンを採用しました。(実は後付け)
これとenumのおかげで今後も変更がだいぶ楽になりそうです。Protocolくんありがとう。
protocolの何が便利かというと後からの変更がとても楽です。追加も楽です。
protocolで決まりを作っておけば後はボーッとコーディングできる。
import Foundation import UIKit public protocol CheckableCellProtocol: UICollectionViewCell { ///cellの細かな表示を変更する var cellStyle: CellStyle { get set } ///cellとcontentViiewの間のマージン var margin: CGFloat { get } ///collectionviewに最初からあるview var contentView: UIView { get } ///textを表示するために一番中心に配置されるview var textLabel: UILabel { get } ///アニメーションを行うためのプロトコル var animationProtocol: TouchCellAnimationProtocol! { get set } ///labelに表示するテキストをセットする func setTextToTextLabel(textName: String) ///選択時の色設定 func selectedColor(text textColor: CellColor?, back backColor: CellColor?, line lineColor: CellColor?) ///非選択時の色設定 func unSelectedColor(text textColor: CellColor?, back backColor: CellColor?, line lineColor: CellColor?) ///cellを設定 func setCell() ///normalstyleの設定。cellにぴったりくっつく func setNormalStyle() ///groovestyleの設定。cellとの間に少し溝を作る func setGrooveStyle() } ///共通してるものはもう作っておく extension CheckableCellProtocol { public var margin: CGFloat { return LayoutConstants.margin } public func setTextToTextLabel(textName: String) { textLabel.text = textName } public func selectedColor(text textColor: CellColor?, back backColor: CellColor?, line lineColor: CellColor?) { textLabel.textColor = textColor?.selectedColor ?? .white contentView.backgroundColor = backColor?.selectedColor ?? .init(red: 255 / 255, green: 100 / 255, blue: 100 / 255, alpha: 1) self.layer.borderColor = lineColor?.selectedColor?.cgColor ?? UIColor.gray.cgColor } public func unSelectedColor(text textColor: CellColor?, back backColor: CellColor?, line lineColor: CellColor?) { textLabel.textColor = textColor?.unSelectedColor ?? .gray contentView.backgroundColor = backColor?.unSelectedColor ?? .init(red: 230 / 255, green: 230 / 255, blue: 230 / 255, alpha: 1) self.layer.borderColor = lineColor?.unSelectedColor?.cgColor ?? UIColor.gray.cgColor } public func setCell() { switch cellStyle { case .normal: setNormalStyle() case .groove: setGrooveStyle() } } public func setNormalStyle() { textLabel.frame = contentView.bounds } public func setGrooveStyle() { contentView.frame = CGRect(x: self.bounds.minX + margin, y: self.bounds.minY + margin, width: self.bounds.width - margin * 2, height: self.bounds.height - margin * 2) textLabel.frame = contentView.bounds } }protocolのextensionを使えば、必ず同じな部分の実装を楽に共通化できるのでとても便利。まぁclassを継承しても良いんだけどprotocolは直接インスタンス化できないところが好き。
ストラテジーパターン
ストラテジーパターンを使うと同じようなものをいくつも作成する時に追加が楽。変更も楽。
やり方は、ルール(プロトコル)を作ってそのルールに則って種類の分かれる奴らをそれぞれ作る。今回で言えばCheckableCellProtocolを作ってそれに則ったcellをSquare, Curve, Round, Circleと作った。それらを利用する側は外部からそのプロトコルに則ったインスタンスを貰うかそれを特定できる情報をもらうようにすれば完成。後は簡単に追加削除変更なんでもござれ。
具体的には上記のgetCellType関数が肝になっているのでgithubから見てみてね。さてこのライブラリの完成度は如何程か?
良かった点
- 割と使いやすいのでは?
- 変更しやすかった。楽しくコーディングできた。
- 自分の理想にかなり近い。
改善点
- はいまずUITestが通りません。意味がわかりません。調べたところdelegateが動作していないのが原因っぽいのですがだからと言ってどうすれば解決するのかさっぱりわからない。要勉強ですね。
- delegateでタグ同士の間のスペースとか指定できるようにしたい。
- github全部ニホンゴ。
- コードレイアウトマンなのでstoryboardで使えるか確認していない。。(近日中に必ず確認して使えるようにするつもりではあるが)
- CellTypeもCellStyleももっと種類増やしたい。
- タグ左寄せの設定もできるようにしたい。
自己評価は120点!良くがんばりました。これからも頑張りましょう!(文句言うな。)
初心者をはじめこの記事を読んで下さった皆様方。
軽い感じで読みやすくしようと思って書いていましたがそうでもなくなってしまいましたね。
もっと色々書きたかったけど難しいし体力切れや。
飛ばし飛ばしでもここまで読んで下さってありがとうございました。解説の部分はあんまり参考にならなかったと思います。定期的にアウトプットしてしっかり言語化することが大事だと感じました。
改善点はまだまだありますがぜひ一度使ってみて下さい。そしてissueやPR投げてくださると嬉しいです。なぜ「初心者をはじめ」と書いたかと言いますと、このコードは簡単なコードで書かれています。まぁ初心者が書いている+他人のコードなので読みにくいかも知れませんが。もっとプログラミング出来るようになりたいと考えている方は、OSSにPR投げたりしてcontributionを重ねていくことって大事だと思うのでそれのとっかかりとして、まずこのライブラリにPRを投げるのも良いと思うよってことが言いたかったです。
READMEのちょっとした修正やタイポ修正などでもこちらとしては本当に助かりますしいわゆるwin-winですね。(古い?)こんなのどうや?って気軽に投げて下さい。※この記事の内容は変なこと言っている可能性も大いにあります。情報の取捨選択をうまく行って、僕には文句ではなく指摘をしてくださると踊って喜びます。
長文駄文失礼しました。ありがとうございました。
- 投稿日:2020-03-15T21:08:17+09:00
swift コードで単色の画像を生成する方法
var image: UIImage? { let rect = CGRect(x: 0, y: 0, width: 1, height: 1) UIGraphicsBeginImageContext(rect.size) guard let context = UIGraphicsGetCurrentContext() else { return nil } context.setFillColor(UIColor(red: 1, green: 1, blue: 1, alpha: 0.8).cgColor) context.fill(rect) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image }
- 投稿日:2020-03-15T17:56:07+09:00
Xcode11でUIScrollViewのAutoLayoutの制約をかける
はじめに
UIScrollViewのAutoLayoutの制約をかける場合、かけ方にクセがあり、かつ作業途中でエラーが発生するため、少し手こずるんですよね。
ここではXcode11でUIScrollViewのAutoLayoutの制約をかける方法を記載します。チュートリアル
環境
- Xcode11
手順
1. ScrollViewを配置
ViewControllerのView上にScrollViewを配置します。
2. ScrollViewにAutoLayoutの制約をかける
ViewController上のSafeAreaとScrollViewの両方を選択します。
画面右下のAlignボタンを選択し、Top/Bottom/Trailling/Leadingの4辺に制約をかけます。
ここでは0を設定します。
かけた後IB上でエラーが発生しますが、一旦無視して下さい。
3. Viewを配置
ScrollView上にViewを配置します。
ここではScrollViewとの違いを分かりやすくするため、Viewの背景色を赤色に設定しています。
4. ViewのAutoLayoutの制約をかける
ScrollViewのContentLayoutGuideとViewの両方を選択し、画面右下のAlignボタンから、Top/Bottom/Trailling/Leadingの4辺に制約をかけます。
次にViewを選択しながら右クリックでScrollViewのFrameLayoutGuideへドラッグ&ドロップし、Equal Widthを選択します。
最後にViewを選択し、画面右下のAdd new constraintsボタンから、Heightに制約をかけます。
ここでは1000を設定します。
- 投稿日:2020-03-15T15:22:44+09:00
iOSアプリを作って公開してみたいと思う
タイトル通りです。Swift初心者がアプリを公開するまでに躓いたところを記録していきたいと思います。
現時点の著者スペック:
2年前にSwiftにチャレンジしようとして挫折。その後、真面目に勉強した言語はPythonのみ。ここ数週間Swift文法本を読むなかで「構造体」などのPythonになかった概念で頭がいっぱいになっている。作りたいアプリ:
1. iPhoneアプリ
Youtube、ニコニコ動画をみながらブラウザを使えるようにしたい。(同じ発想のアプリは公開されているけど、現時点ではDLはできるけど動かなくなっている) https://apps.apple.com/jp/app/netube/id937049763
2. Macアプリ
ブラウザ閲覧履歴から、各サイトの関係性を自動的に時系列でまとめて一覧表示してくれるようなアプリが欲しい。レポートを書くのに便利そうなので。(これについてはSwiftで作るのが良いのかすらよくわかってない。)
- 投稿日:2020-03-15T15:22:44+09:00
iOSを作って公開してみたいと思う
タイトル通りです。Swift初心者がアプリを公開するまでに躓いたところを記録していきたいと思います。
現時点の著者スペック:
2年前にSwiftにチャレンジしようとして挫折。その後、真面目に勉強した言語はPythonのみ。ここ数週間Swift文法本を読むなかで「構造体」などのPythonになかった概念で頭がいっぱいになっている。作りたいアプリ:
1. iPhoneアプリ
Youtube、ニコニコ動画をみながらブラウザを使えるようにしたい。(同じ発想のアプリは公開されているけど、現時点ではDLはできるけど動かなくなっている) https://apps.apple.com/jp/app/netube/id937049763
2. Macアプリ
ブラウザ閲覧履歴から、各サイトの関係性を自動的に時系列でまとめて一覧表示してくれるようなアプリが欲しい。レポートを書くのに便利そうなので。(これについてはSwiftで作るのが良いのかすらよくわかってない。)
- 投稿日:2020-03-15T04:14:34+09:00
Android/iOSストア情報まとめ(2020.03)
はじめに
アプリを新規公開する際に必要な情報をまとめました。
ストア情報はデザイナーや非エンジニアが決めることが多いため、そのような方たちが読めるようにまとめます。また、ストア情報の他にも、決めるべき項目について記載しました。※オプショナルな情報については薄い文字で記載しています。
アプリ情報
Android iOS 備考 正式名称 ~50文字 ~23文字 SEO対策しすぎるとリジェクトされる可能性があります 短縮名称 ~12文字程度 ~13文字 アプリアイコンの下に表示される名前。
機種などにより表示できる文字数は変わりますサブタイトル なし ~30文字 iOS11以降で表示されます 更新情報 ~500文字 ~4000文字 初回リリース時も必要となります ID Package id Bundle id Androidの場合、ストアURLに含まれます。
一度決めると変更はできません。例えばGoogleMapの場合はcom.google.android.apps.maps
となっています忘れがちなのが
更新情報
です。初回リリースですが、こちらの文言も必要となりますのでご注意ください。
また、プライマリー言語の設定と、各項目の翻訳も必要となります。
説明文
Android iOS 備考 詳しい説明 ~4000文字 ~4000文字 簡単な説明 ~80文字 ~170文字 iOSでは プロモーション用テキスト
キーワード なし ~100文字 SEO対策
- プロモーションテキストについて
- iOS11以降。審査なしで現在のアプリ機能について知らせることができるようです。
ストア画像(Androidのみ)
サイズ 形式 用途 フィーチャーグラフィック 横1,024x縦500 JPG or 24bit PNG(アルファなし) ストアの アプリタブ
でフィーチャーされる際に使用。
必須プロモーション画像 横180x縦120 JPG or 24bit PNG(アルファなし) ストアの ゲームタブ
でフィーチャーされる際に使用。
オプショナル。URL、連絡先情報
Android iOS 備考 プラポリURL オプショナル 必須 サポートURL なし オプショナル ウェブサイト(Android)/ マーケティングURL(iOS) オプショナル オプショナル Copyright なし 必須 電話 オプショナル なし ストアに表示されます メール 必須 なし ストアに表示されます アプリアイコン
OSごとに大きく異なるため、別々に記載します。
Android
ストア用画像
- 512x512
- 32bit PNG
Android8.0未満
解像度 アイコンサイズ xxxhdpi 192x192 xxhdpi 144x144 xhdpi 96x96 hdpi 72x72 mdpi 48x48 AdaptiveIcon(Android8.0以上)
解像度 サイズ xxxhdpi 432x432 xxhdpi 324x324 xhdpi 216x216
- foregroundとbackgroundの2枚を用意します(サイズは同じ)
- foregroundは直径66がsafe zoneなので、モチーフはそこに収まるようにします
- OS8以上なので、ある程度高解像度の画像だけで十分
iOS
サイズ 用途 20x20 ipad notification用(@1x) 29x29 iPad設定画面用(@1x)
iPhone設定画面用(@1x)40x40 ipad notification用(@2x)
notification用(@2x)
iPad Spotlight用(@1x)58x58 iPad設定画面用(@2x)
iPhone設定画面用(@2x)60x60 notification用(@3x) 76x76 iPadAppIcon(@1x) 80x80 iPad Spotlight用(@2x)
Spotlight用(@2x)87x87 設定画面用(@3x) 152x152 iPadAppIcon(@2x) 120x120 Spotlight用(@3x)
AppIcon(@2x)167x167 iPadProAppIcon(@2x) 180x180 AppIcon(@3x) 1024x1024 ストア用 スクリーンショット
スマートフォンの場合のみ記載します。必要に応じてタブレット、Wear、TVなども設定可能です。
また、各言語でローカライズされた画像を用意しましょう。
Android iOS サイズ 1 辺の最小の長さ: 320px
1 辺の最大の長さ: 3840px
アス比は1:2以下(縦長すぎ/横長すぎるのはエラー)後述 形式 JPG/PNG(アルファなし) JPG/PNG(アルファなし) 枚数 2枚(最大8枚) 各サイズ最低1枚、最大10枚 iOSの場合は以下のサイズで揃える必要があります。
サイズ 枚数 該当端末 6.5インチ 1242 x 2688 最低1枚、最大10枚 iPhone 11 Pro Max, iPhone 11, iPhone XS Max, iPhone XR 5.5インチ 1242 x 2208 最低1枚、最大10枚 iPhone 6s Plus、iPhone 7 Plus、iPhone 8 Plus
- 投稿日:2020-03-15T04:14:34+09:00
Android/iOSストア情報まとめ(2020年03月更新)
はじめに
アプリを新規公開する際に必要な情報をまとめました。
ストア情報はデザイナーや非エンジニアが決めることが多いため、そのような方たちが読めるようにまとめます。また、ストア情報の他にも、決めるべき項目について記載しました。
とはいえ、本記事では基本的に必要最低限に近いレベルのことしか記載していませんので、実際には様々なカスタマイズ情報を掲載してアプリのグロースをしてみてください。※オプショナルな情報については薄い文字で記載しています。
アプリ情報
Android iOS 備考 正式名称 ~50文字 ~23文字 SEO対策しすぎるとリジェクトされる可能性があります 短縮名称 ~12文字程度 ~13文字 アプリアイコンの下に表示される名前。
機種などにより表示できる文字数は変わりますサブタイトル なし ~30文字 iOS11以降で表示されます 更新情報 ~500文字 ~4000文字 初回リリース時も必要となります ID Package id Bundle id Androidの場合、ストアURLに含まれます。
一度決めると変更はできません。例えばGoogleMapの場合はcom.google.android.apps.maps
となっています忘れがちなのが
更新情報
です。初回リリースですが、こちらの文言も必要となりますのでご注意ください。
また、プライマリー言語の設定と、各項目の翻訳も必要となります。
説明文
Android iOS 備考 詳しい説明 ~4000文字 ~4000文字 簡単な説明 ~80文字 ~170文字 iOSでは プロモーション用テキスト
キーワード なし ~100文字 SEO対策
- プロモーションテキストについて
- iOS11以降。審査なしで現在のアプリ機能について知らせることができるようです。
ストア画像(Androidのみ)
サイズ 形式 用途 フィーチャーグラフィック 横1,024x縦500 JPG or 24bit PNG(アルファなし) ストアでフィーチャーされる際に使用。
必須![]()
プロモーション画像 横180x縦120 JPG or 24bit PNG(アルファなし) 古い端末のストアでフィーチャーされる際に使用されます。今となっては用意しなくてもよいと思います フィーチャーグラフィックはかつてストアのヘッダー画像として使われていましたが、ストアの仕様変更により、フィーチャー時にのみ使われるようになりました。
URL、連絡先情報
Android iOS 備考 プラポリURL オプショナル 必須 サポートURL なし オプショナル ウェブサイト(Android)/ マーケティングURL(iOS) オプショナル オプショナル Copyright なし 必須 電話 オプショナル なし ストアに表示されます メール 必須 なし ストアに表示されます
アプリアイコン
OSごとに大きく異なるため、別々に記載します。
Android
Androidはストア用に高解像度が1枚、アプリに組み込む画像が解像度ごとに必要です。また、OS8以降とその前でアイコンの仕様が異なります。
ストア用画像
- 512x512
- 32bit PNG
Android8.0未満
解像度 アイコンサイズ xxxhdpi 192x192 xxhdpi 144x144 xhdpi 96x96 hdpi 72x72 mdpi 48x48 AdaptiveIcon(Android8.0以上)
解像度 サイズ xxxhdpi 432x432 xxhdpi 324x324 xhdpi 216x216
- foregroundとbackgroundの2枚を用意します(サイズは同じ)
- foregroundは直径66がsafe zoneなので、モチーフはそこに収まるようにします
- OS8以上なので、ある程度高解像度の画像だけで十分
iOS
サイズ 用途 20x20 ipad notification用(@1x) 29x29 iPad設定画面用(@1x)
iPhone設定画面用(@1x)40x40 ipad notification用(@2x)
notification用(@2x)
iPad Spotlight用(@1x)58x58 iPad設定画面用(@2x)
iPhone設定画面用(@2x)60x60 notification用(@3x) 76x76 iPadAppIcon(@1x) 80x80 iPad Spotlight用(@2x)
Spotlight用(@2x)87x87 設定画面用(@3x) 152x152 iPadAppIcon(@2x) 120x120 Spotlight用(@3x)
AppIcon(@2x)167x167 iPadProAppIcon(@2x) 180x180 AppIcon(@3x) 1024x1024 ストア用 スクリーンショット
スマートフォンの場合のみ記載します。必要に応じてタブレット、Wear、TVなども設定可能です。
また、各言語でローカライズされた画像を用意しましょう。
Android iOS サイズ 1 辺の最小の長さ: 320px
1 辺の最大の長さ: 3840px
アス比は1:2以下(縦長すぎ/横長すぎるのはエラー)後述 形式 JPG/PNG(アルファなし) JPG/PNG(アルファなし) 枚数 2枚(最大8枚) 各サイズ最低1枚、最大10枚 iOSの場合は以下のサイズで揃える必要があります。
サイズ 枚数 該当端末 6.5インチ 1242 x 2688 最低1枚、最大10枚 iPhone 11 Pro Max, iPhone 11, iPhone XS Max, iPhone XR 5.5インチ 1242 x 2208 最低1枚、最大10枚 iPhone 6s Plus、iPhone 7 Plus、iPhone 8 Plus
- 投稿日:2020-03-15T03:58:15+09:00
[Swift] 同一ドメインでのユニバーサルリンク(Universal Links)は動作しないので注意メモ
Web側とiOS側でユニバーサルリンクを設定したが何故か動作せず、調べたら同一ドメイン上では機能してくれない様でした。。
実装前の調査不足ということで、備忘録的なメモになります。概要
- 同一ドメインではユニバーサルリンクは機能しない(アプリが起動しない)
- サブドメインを使った代替策
同一ドメイン上ではアプリへ遷移ができない
ユニバーサルリンクは同一ドメイン上では動作しないため注意が必要です。
公式ドキュメントでも注意点として記載されていました。。
すでにWebで利用しているユーザーに対して急にアプリ起動したらUX的によろしくないのでAppleさんが制御しているらしいです。
(翻訳した自分の解釈が間違っていなければ)Support Universal Links:
https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.htmlサブドメインをリンク先に設定することで、ユニバーサルリンクを有効にすることができます。
とはいえ、サブドメインで分けるほどのページではないがユニバーサルリンクを有効にしたい場面があるかと思います。
次では、見た目上は同一ドメインでユニバーサルリンクが動作する例を紹介します。代替策
同一ドメインではユニバーサルリンクは無効ですが、サブドメインなどに遷移先を設定すれば有効になります。
以下例では、見た目上は同一ドメインでの遷移ですがユニバーサルリンクをサブドメインとしている代替策になります。# shop一覧画面(www.hoge.com)から詳細画面を開く場面を想定しています。 # 詳細画面のリンク先をサブドメイン(shop.hoge.com/shop_id)として設定。 # リンク先のサブドメインはリダイレクト先として詳細画面(www.hoge.com/shop/shop_id)を設定。 # 設定したリンクはユニバーサルリンクとして設定しているため、アプリが起動します。 # アプリ未インストール&Webでの表示では、リダイレクト先の画面(www.hoge.com/shop/shop_id)が表示されます。まとめ
- ユニバーサルリンクは同一ドメインでは機能しない
- 機能させるためには、サブドメインなど活用する(場面に応じてリダイレクト先を設定するなど対応する)
- ユニバーサルリンクを使用しない方法(URLスキーム)で実装できないかを考える
記載した情報に誤り等ございましたら、ご指摘をいただけると幸いですmm
- 投稿日:2020-03-15T02:57:52+09:00
Swift UI で SearchBar を使う
Search Bar をつけたい
SwiftUI でUISearchBarをつけようとすると、存外に苦労しました。おそらくまだ十分にAPIが開放されていないためだと思います。こういう場合、
UIViewControllerRepresentable
などを使い、SwiftUIで使えるようにしていくしかないのかなぁと思います。やりたいのは以下の通り、UIKit使ってたら普通にできたよね・・・?
tl;dr
- SearchBarを使いたい時はUINavigationControllerをUIViewControllerRepresentableでラップする。その際にUISearchControllerをセット
- NavigationBarしか使用しない場合は、親ViewのSafeAreaを
edgesIgnoringSafeArea(.vertical)
で調整- TabBarをでNavigationBarを囲っている場合は、親ViewのSafeAreaを
edgesIgnoringSafeArea(.top)
で調整後、NavigationBarが実装されているViewのSafeAreaをedgesIgnoringSafeArea(.vertical)
で調整する1. SearchBarをSwiftUIで使用する
- まずUINavigationViewControllerにUISearchControllerを追加して、それををSwiftUIで使用できるようにします。
struct NavigationViewControllerRepresentation: UIViewControllerRepresentable { typealias UIViewControllerType = UINavigationController func makeUIViewController(context: Context) -> UINavigationController { let viewController = UIHostingController(rootView: List{ Text("one").frame(height: 120.0) ... Text("six").frame(height: 120.0) }.navigationBarTitle("Title")) let navigationController = UINavigationController(rootViewController: viewController) navigationController.navigationBar.prefersLargeTitles = true viewController.navigationItem.searchController = UISearchController() return navigationController } func updateUIViewController(_ uiViewController: UINavigationController, context: Context) { } }
- SwiftUI Canvasに表示させる
MyNavigationView.swiftstruct MyNavigationView: View { var body: some View { NavigationViewControllerRepresentation() } } struct MyNavigationView_Previews: PreviewProvider { static var previews: some View { MyNavigationView() .environment(\.colorScheme, .dark) // わかりやすくするためにダークモードで表示 } }2. SafeAreaを調整する
この状態だと、赤枠で囲ったように上下に隙間ができます。SafeAreaですね。
MyNavigationView.edgesIgnoringSafeArea(.vertical)
としたいところですがここではその衝動をぐっと抑えます。SceneDelegate.swiftclass SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo _: UISceneSession, options _: UIScene.ConnectionOptions) { let contentView = MyNavigationView().edgesIgnoringSafeArea(.vertical) // [1] if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.rootViewController = UIHostingController(rootView: contentView) self.window = window window.makeKeyAndVisible() } }コメントの[1]にあるように、最下層のビューに対して
edgesIgnoringSafeArea(.vertical)
を適用してあげます。そうするとプレビュー上は隙間がありますが、実機で動かすと確かに上下の隙間がなくなります。
- 確認結果
右側のスクリーンショット、ステータスバーのところまで暗めの灰色になってるのが見えるでしょうか。
3. TabBarに対応させる
TabBarが絡んでくるとちょっとまた調整が必要になります。試しにそのままTabBarで囲い込んでみます。
MyNavigationView.swiftstruct MyNavigationView: View { var body: some View { /*NavigationViewControllerRepresentation()*/ TabView { NavigationViewControllerRepresentation().tabItem { Image(systemName: "1.circle") Text("tab1").tag(0) } Text("tab2").tabItem { Image(systemName: "2.circle") Text("tab2").tag(1) } } } }
![]()
ちょっと上下にTabBarとNavigationBarが食い込んでますね。これはどういわけか.top
で直ります。SceneDelegate.swiftclass SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo _: UISceneSession, options _: UIScene.ConnectionOptions) { // let contentView = MyNavigationView().edgesIgnoringSafeArea(.vertical) let contentView = MyNavigationView().edgesIgnoringSafeArea(.top) if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.rootViewController = UIHostingController(rootView: contentView) self.window = window window.makeKeyAndVisible() } }
![]()
...やった、、、のか?と思いましたが右側のスクリーンショットを見てください。ステータスバーのところが暗めの灰色ではなく、黒色になっています。そこで、また調整をします。MyNavigationView.swiftstruct MyNavigationView: View { var body: some View { /*NavigationViewControllerRepresentation()*/ TabView { NavigationViewControllerRepresentation().tabItem { Image(systemName: "1.circle") Text("tab1").tag(0) } .edgesIgnoringSafeArea(.vertical) // 追加 Text("tab2").tabItem { Image(systemName: "2.circle") Text("tab2").tag(1) } } } }やりましたね。