20200915のiOSに関する記事は8件です。

異なる階層の View の座標を取得しよう

今回も初学者向けに階層の異なる特定の View の座標を取得したい時に使用する convert() 関数の使い方を簡単に紹介したいと思います。

実際に使ってみる?‍?

下記のような階層の View を参考に convert() メソッドを使って見て座標を取得してみます。

緑色からみた黄色

これは、そもそも緑色の View の中に 黄色の View が含まれている(addSubView()されている)ので、黄色の View の Frame を取得すれば座標が分かります。

let greenToYellowRect = yellowView.frame
print(greenToYellowRect.toString) // (x: 19.0, y: 38.5)

また、toString というプロパティは下記のように CGRect の Extension プロパティとして追加すると独自に値を取得するためのプロパティを追加することができるようになります? 特に iOS 開発ではこの Extension 記法は頻繁に使用するので、どんどん書いて使い方をマスターしましょう!

extension CGRect {
    var toString: String {
        return "(x: \(self.origin.x), y: \(self.origin.y))"
    }
}

赤色からみた黄色

次に、赤色の View から見た黄色の View の座標を取得していきましょう。先ほどと同じように frame を使って座標を取得したいところですが、今回は赤色から見た黄色の座標を取得したいので、frame で取得してしまうと緑色から見た黄色になってしまうので気をつけましょう。

それでは早速、convert()メソッドを使って赤色から見た黄色の座標を取得してみます。

let redToYellowRect = greenView.convert(yellowView.frame, to: redView)
print(redToYellowRect.toString) // (x: 94.5, y: 146.0)

それぞれの View の指定方法は、最後のセクションにまとめてあります。

白色からみた黄色

次に、白色の View からみた黄色の View の座標を取得していきます。コードは下記のようになります。

let whiteToYellowRect = greenView.convert(yellowView.frame, to: self.view)
print(whiteToYellowRect.toString) // (x: 181.5, y: 315.0)

それぞれの View からみた黄色の View の正しい座標が取れるようになりました?

まとめ

convert() メソッドは、どこにどの View を指定するのか忘れがちなので下記のように覚えておくと便利です。

convert(to:) の場合

let redToYellowRect = 座標を知りたいViewの親View.convert(座標を知りたいView.frame, to: 座標変換の基準となるView)

convert(from:) の場合

let redToYellowRect = 座標変換の基準となるView.convert(座標を知りたいView.frame, from: 座標を知りたいViewの親View)

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【1日目】Youtubeアプリ開発日記(iOS) / 各種設定からYoutube API をアプリからリクエスト

概要

iPhoneアプリ開発を行う人のために、教材となればいいなと思い、この記事を書いていきます。
つくっていくアプリは、YoutubeのiPhoneアプリを開発します。

この記事のゴール

  • YoutubeのAPIを使えるようにする
  • iPhoneアプリでYoutubeのAPIをリクエストできるようにする

アプリの使用ライブラリを先に紹介

  • Alamofire (API通信ライブラリ)
  • Moya/RxSwift (Alamofire補完ライブラリ)
  • RxSwift (リアクティブプログラミング)

準備

いきなりハードルが高くなりそうですが、まずはYoutube Data API を登録します。
https://developers.google.com/youtube/v3/getting-started?hl=ja

手順

  1. Googleアカウントで、Google Cloud Console へのアクセス
  2. プロジェクトを作成
  3. API Keyをこちらで作成
    1. API Keyは、アプリで使うのでメモしておきましょう。
  4. Services から、YouTube Data API を検索して、ステータスをオンにする

補足

Google Cloud Consoleで、プロジェクト作成

スクリーンショット 2020-09-15 20.25.22.png
スクリーンショット 2020-09-15 20.24.04.png

API を試してみよう

API Keyを作成できたら、そのKeyを使ってAPIが実際に使えるのかを確認しましょう。
APIは、Youtube動画を検索できるAPI (/youtube/v3/docs/search/list) を使います。

APIの構成

https://www.googleapis.com/youtube/v3/search?part=snippet&q={検索キーワード}&key={自分のAPI KEY}

レスポンス例

{
  "kind": "youtube#searchListResponse",
  "etag": "BaxNpbPgVkyQQVmx4qeEzGdzCTo",
  "nextPageToken": "CAEQAA",
  "regionCode": "JP",
  "pageInfo": {
    "totalResults": 1000000,
    "resultsPerPage": 1
  },
  "items": [
    {
      "kind": "youtube#searchResult",
      "etag": "MkmmQLdB1WA8Icy7XbfCfXQcdBI",
      "id": {
        "kind": "youtube#channel",
        "channelId": "UCZf__ehlCEBPop-_sldpBUQ"
      },
      "snippet": {
        "publishedAt": "2011-07-19T11:31:43Z",
        "channelId": "UCZf__ehlCEBPop-_sldpBUQ",
        "title": "HikakinTV",
        "description": "HikakinTVはヒカキンが日常の面白いものを紹介するチャンネルです。 ◇プロフィール◇ YouTubeにてHIKAKIN、HikakinTV、HikakinGames、HikakinBlogと 4つの ...",
        "thumbnails": {
          "default": {
            "url": "https://yt3.ggpht.com/-NFhw6-eus8Y/AAAAAAAAAAI/AAAAAAAAAAA/rtPbnb9gvAQ/s88-c-k-no-mo-rj-c0xffffff/photo.jpg"
          },
          "medium": {
            "url": "https://yt3.ggpht.com/-NFhw6-eus8Y/AAAAAAAAAAI/AAAAAAAAAAA/rtPbnb9gvAQ/s240-c-k-no-mo-rj-c0xffffff/photo.jpg"
          },
          "high": {
            "url": "https://yt3.ggpht.com/-NFhw6-eus8Y/AAAAAAAAAAI/AAAAAAAAAAA/rtPbnb9gvAQ/s800-c-k-no-mo-rj-c0xffffff/photo.jpg"
          }
        },
        "channelTitle": "HikakinTV",
        "liveBroadcastContent": "upcoming",
        "publishTime": "2011-07-19T11:31:43Z"
      }
    }
  ]
}

Firebaseプロジェクトを設定

Firebaseコンソール にアクセスして、自分のアプリのプロジェクトを作成しましょう。
ドキュメントのSTEP1 ~ STEP5までを行えば、基本的には問題ないですが、Firebaseは親切なので、以下の画像のように順番にやるべきことを指示してくれます。
スクリーンショット 2020-09-15 21.22.43.png

アプリを作成

Xcodeをインストールして、アプリの初期画面を作成するというのは、こちらの記事などを参考にしてみてください。

ライブラリをインストール

Firebaseの初期設定で、すでにcocoapodsでPodfileを作成していると思いますので、cocoapodsを使ってライブラリを追加していきます。Podfileの基本的な使い方はこちらの記事を参考にしてみると良いでしょう。

それでは、実際にPodfileに、前述のライブラリを追加しましょう。

target 'アプリ名' do
  pod 'Firebase/Analytics'
  pod 'Alamofire', '~> 5.2'
  pod 'Kingfisher', '~> 5.0'
  pod 'Moya/RxSwift', '~> 14.0'
end

これで、pod install をしてください。
アプリ.xcworkspaceを開けば、インストールしたライブラリが使える状態になっています。

ようやくコードが書けます!お疲れ様です!

UIViewControllerにAPIリクエストを実装

import UIKit
import Alamofire

class ViewController: UIViewController {
    private let apiKey = "xxxxxxxxx"

    override func viewDidLoad() {
        super.viewDidLoad()

        let keyword = "ヒカキン"
        let urlString = "https://www.googleapis.com/youtube/v3/search?part=snippet&q=\(keyword)&key=\(apiKey)"
        let request = AF.request(urlString)

        request.responseJSON { response in
            print(response)
        }
    }
}

Youtube API の通信

ここでAPI通信処理を担うライブラリAlamofireを使うためにインポートします。

import Alamofire

あとは簡単。↓で、リクエストするURLを生成します。

let keyword = "ヒカキン"
let urlString = "https://www.googleapis.com/youtube/v3/search?part=snippet&q=\(keyword)&key=\(apiKey)"

↓で、APIにリクエストします。

let request = AF.request(urlString)

最後に、受け取ったレスポンスを処理します。
ここでは、レスポンスをログに出力するのみにしています。

request.responseJSON { response in
    print(response)
}

レスポンス

以下のように、コンソールにログが出力されたら成功です。

success({
    etag = "1pYoINxiqsB91hilou77FGlS_1E";
    items =     (
                {
            etag = MkmmQLdB1WA8Icy7XbfCfXQcdBI;
            id =             {
                channelId = "UCZf__ehlCEBPop-_sldpBUQ";
                kind = "youtube#channel";
            };
            kind = "youtube#searchResult";
            snippet =             {
                channelId = "UCZf__ehlCEBPop-_sldpBUQ";
                channelTitle = HikakinTV;
                description = "HikakinTV\U306f\U30d2\U30ab\U30ad\U30f3\U304c\U65e5\U5e38\U306e\U9762\U767d\U3044\U3082\U306e\U3092\U7d39\U4ecb\U3059\U308b\U30c1\U30e3\U30f3\U30cd\U30eb\U3067\U3059\U3002 \U25c7\U30d7\U30ed\U30d5\U30a3\U30fc\U30eb\U25c7 YouTube\U306b\U3066HIKAKIN\U3001HikakinTV\U3001HikakinGames\U3001HikakinBlog\U3068 \Uff14\U3064\U306e ...";
                liveBroadcastContent = upcoming;
                publishTime = "2011-07-19T11:31:43Z";
                publishedAt = "2011-07-19T11:31:43Z";
                thumbnails =                 {
                    default =                     {
                        url = "https://yt3.ggpht.com/-NFhw6-eus8Y/AAAAAAAAAAI/AAAAAAAAAAA/rtPbnb9gvAQ/s88-c-k-no-mo-rj-c0xffffff/photo.jpg";
                    };
                    high =                     {
                        url = "https://yt3.ggpht.com/-NFhw6-eus8Y/AAAAAAAAAAI/AAAAAAAAAAA/rtPbnb9gvAQ/s800-c-k-no-mo-rj-c0xffffff/photo.jpg";
                    };
                    medium =                     {
                        url = "https://yt3.ggpht.com/-NFhw6-eus8Y/AAAAAAAAAAI/AAAAAAAAAAA/rtPbnb9gvAQ/s240-c-k-no-mo-rj-c0xffffff/photo.jpg";
                    };
                };
                title = HikakinTV;
            };
        }
    );
    kind = "youtube#searchListResponse";
    nextPageToken = CAEQAA;
    pageInfo =     {
        resultsPerPage = 1;
        totalResults = 577518;
    };
    regionCode = JP;
})
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSからQiitaのAPIを呼ぼうとしたら懐かしいエラーに遭遇した。未来の誰かのためにここに記す

iOS懐かしのエラー

昔よく見たエラーにふとした拍子に出くわししました:yum: 1

App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.

これはメッセージの通り、HTTP通信をしようとしたときにiOSがブロックしてくる奴です。

QiitaのAPIを呼ぼうとしていた

URLSessionに渡しているurlは間違いなくhttpsのはず……:thinking:

let url = URL(string: "https://qiita.com/api/v2/items/?per_page=3")!
let task = urlSession.dataTask(with: url) { data, response, error in
    ...

エラーメッセージを確認してみる

- Error Domain=NSURLErrorDomain Code=-1022 "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." UserInfo={NSUnderlyingError=0x6000007b4b40 {Error Domain=kCFErrorDomainCFNetwork Code=-1022 "(null)"}, NSErrorFailingURLStringKey=http://qiita.com/api/v2/items, NSErrorFailingURLKey=http://qiita.com/api/v2/items, NSLocalizedDescription=The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.}

:astonished:?!:astonished:

NSErrorFailingURLKey=http://qiita.com/api/v2/items?per_page=3

なぜだかわかりませんが、URLスキームがhttpに変わっています???

そして、ローカルホストに繋いだりするときと同じように、App Transport Security Settings の Allow Arbitrary Loads を YES に設定すると問題なく結果が返ってきました。

犯人は……?

小一時間ハマったのち、ようやく犯人が見つかりました。2

let url = URL(string: "https://qiita.com/api/v2/items/?per_page=3")!

:rolling_eyes: よく見ると、URLの末尾にスラッシュがついている……? :rolling_eyes:

え?でも、このURLをcurlで叩いても普通にレスポンスが返ってきてたような?

もう一度curlで叩いてみた

$ curl -D - https://qiita.com/api/v2/items/ 
HTTP/2 301 
date: Tue, 15 Sep 2020 12:45:32 GMT
content-type: text/html
content-length: 178
location: http://qiita.com/api/v2/items
server: nginx

<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>

:astonished:?!:astonished:

:innocent:なぜかhttpにリダイレクトされています:innocent:3

QiitaのAPIを使うときは、末尾のスラッシュに気をつけましょう

という結論でした:see_no_evil:

let url = URL(string: "https://qiita.com/api/v2/items?per_page=3")!

万が一同じ現象に遭遇した人がいれば、ここに辿り着けますように。


  1. リリース前の開発ではいまでもよく見るかもしれませんね。 

  2. 当初はmacOSのプロジェクトで試していたため、普段触ることのないサンドボックスの設定などを疑っていたため、どハマりしてしまいました。 

  3. そういえば調査の途中でcurl使ったときには、無意識に-Lオプションをつけていたような……:thinking: 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

QiitaのAPIを呼ぶときに末尾にスラッシュをつけるとhttp://~にリダイレクトされるので気をつけましょう

TL;DR

タイトル通り。curlの結果はこちら

iOS懐かしのエラー

昔よく見たエラーにふとした拍子に出くわししました:yum: 1

App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.

これはメッセージの通り、HTTP通信をしようとしたときにiOSがブロックしてくる奴です。

QiitaのAPIを呼ぼうとしていた

URLSessionに渡しているurlは間違いなくhttpsのはず……:thinking:

let url = URL(string: "https://qiita.com/api/v2/items/?per_page=3")!
let task = urlSession.dataTask(with: url) { data, response, error in
    ...

エラーメッセージを確認してみる

- Error Domain=NSURLErrorDomain Code=-1022 "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." UserInfo={NSUnderlyingError=0x6000007b4b40 {Error Domain=kCFErrorDomainCFNetwork Code=-1022 "(null)"}, NSErrorFailingURLStringKey=http://qiita.com/api/v2/items, NSErrorFailingURLKey=http://qiita.com/api/v2/items, NSLocalizedDescription=The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.}

:astonished:?!:astonished:

NSErrorFailingURLKey=http://qiita.com/api/v2/items?per_page=3

なぜだかわかりませんが、URLスキームがhttpに変わっています???

そして、ローカルホストに繋いだりするときと同じように、App Transport Security Settings の Allow Arbitrary Loads を YES に設定すると問題なく結果が返ってきました。

犯人は……?

小一時間ハマったのち、ようやく犯人が見つかりました。2

let url = URL(string: "https://qiita.com/api/v2/items/?per_page=3")!

:rolling_eyes: よく見ると、URLの末尾にスラッシュがついている……? :rolling_eyes:

え?でも、このURLをcurlで叩いても普通にレスポンスが返ってきてたような?

もう一度curlで叩いてみた

$ curl -D - https://qiita.com/api/v2/items/ 
HTTP/2 301 
date: Tue, 15 Sep 2020 12:45:32 GMT
content-type: text/html
content-length: 178
location: http://qiita.com/api/v2/items
server: nginx

<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>

:astonished:?!:astonished:

:innocent:なぜかhttpにリダイレクトされています:innocent:3

QiitaのAPIを使うときは、末尾のスラッシュに気をつけましょう

という結論でした:see_no_evil:

let url = URL(string: "https://qiita.com/api/v2/items?per_page=3")!

万が一同じ現象に遭遇した人がいれば、ここに辿り着けますように。


  1. リリース前の開発ではいまでもよく見るかもしれませんね。 

  2. 当初はmacOSのプロジェクトで試していたため、普段触ることのないサンドボックスの設定などを疑っていたため、どハマりしてしまいました。 

  3. そういえば調査の途中でcurl使ったときには、無意識に-Lオプションをつけていたような……:thinking: 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】UILabelをUITextViewに変更する際の注意点(メモ)

UILabelで作っていたが、後々ハイパーリンクにしたいなどで、
UILabel→UITextViewに変更しなくては。。などというケースの注意点メモ。。。

環境

  • Xcode: 11.3.1
  • Swift5

・UITextViewのスクロールをオフ

オートレイアウトを使用している場合、スクロールがONだと高さが計算されないので、
以下コードを追記

    textView.isScrollEnabled = false

・UITextViewの余白(padding)を削除

実際に文字を表示してみると上下に余白ができてしまう。
以下コードを追記

    textView.textContainerInset = .zero

備考

アプリ公開しました!よろしければインストールお願いします。
とらんぽ

Twitter始めました!よろしければフォローお願いします。
@yajima_tohshu

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPickerViewControllerを使った動画の読み込み方法

iOS14で新しく登場したPHPickerViewControllerにおける動画の読み込み方法

PHPickerViewController

iOS14で新しくPHPickerViewControllerが追加されました。写真許可をユーザーからとる必要なく、また、OS標準の写真アプリとおなじUIで写真・動画選択ができるとても便利なものです。
Apple Documentation

動画の読み込み

PHPickerViewControllerの表示

func addButtonTapped(_ sender: Any) {
    var configuration = PHPickerConfiguration()
    configuration.filter = .videos
    configuration.selectionLimit = 1
    configuration.preferredAssetRepresentationMode = .current <- 動画を読み込む時のキモ
    let phPicker = PHPickerViewController(configuration: configuration)
    phPicker.delegate = self
    present(phPicker, animated: true, completion: nil)
}

動画の読み込み方法

  • NSItemProviderを使います
  • resultsのなかにPHAssetのassetLocalIdentifierが入っていますが、それを用いて PHAsset. fetchAssets(withLocalIdentifiers:options:)とやってしまうと写真許可のアラートが表示されてしまいます。
  • 写真許可がいらなく動画を読み込むためにはNSItemProviderを使います。
  • ここで NSItemProvider.loadFileRepresentation(forTypeIdentifier:completionHandler:) を使います。
  • 先ほどのPHPickerViewControllerの表示のところで PHPickerConfiguration.preferredAssetRepresentationModeを .currentに設定しておくのが大事です。ここで.currentに設定しない場合、システムがどのバージョンの動画を読み込むかの判定に時間がかかるため、数秒のタイムラグが発生します。ここで.currentを指定しておけば、1秒以内に動画を再生させることができます。 (参考: https://developer.apple.com/forums/thread/652695?answerId=629922022#629922022)
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        picker.dismiss(animated: true, completion: nil)
        guard let provider = results.first?.itemProvider else { return }
        guard let typeIdentifier = provider.registeredTypeIdentifiers.first else { return }

        if provider.hasItemConformingToTypeIdentifier(typeIdentifier) {
            // not provider.loadInPlaceFileRepresentation(forTypeIdentifier: typeIdentifier) { [weak self] (url, success, error) in
            // provider.loadFileRepresentation should be used
            // PHPickerConfiguration.preferredAssetRepresentationMode should be set .current, otherwise loading takes too long
            // https://developer.apple.com/forums/thread/652695?answerId=629922022#629922022
            provider.loadFileRepresentation(forTypeIdentifier: typeIdentifier) { [weak self] (url, error) in
                if let error = error { print("*** error: \(error)") }
                if let url = url {
                    let asset = AVURLAsset(url: url)
                    // ...
                }
            }
        }
    }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Swift]アプリ起動中に、コードによりホーム画面に戻る方法

はじめに

とあるiOSアプリを触っていると、設定画面に言語変更のオプションがあり興味本位で変更してみるとアプリの再起動を促されて自動でホーム画面に戻るというUXを見つけました。そこでどういう実装をしているのか気になり調べてみました。
完成図↓

アプリを意図的に落とすUXはどうなのか

この点に関しては以下の記事に詳しく書かれていました。
https://news.mynavi.jp/article/20190528-iphone_why/

メモリ不足に起因するリソース不足などについてもシステム側でメモリの開放が行われることから、アプリを落とさなければならないという状況は極めて限定的ではあると考えられます。しかし表示言語の変更などのやむをえない場合についてはこのようなUXを実装しても問題なさそうです。

単純にアプリを落とす方法

ただ単にアプリを落とすだけなら、exit(0)などで落とすことができます。ただし、これだけだとアプリが突然クラッシュしたかのような挙動となりあまりふさわしいとは思えません。

また、exit(0)以外にも、fatalError()やassertなどで落とすことができますが、今回の言語の変更を目的とした、使用用途としては不適だと言えます。これらの使い分けについては以下の記事が大変参考になりましたのでご覧ください。

https://scior.hatenablog.com/entry/2019/04/04/202352

exit(0)//今回使用する
fatalError()//不適
assert(false, "")//不適

そこで

そこでアプリを終了させる前にホーム画面に戻るという処理を行います。こうすることでクラッシュしたかのような挙動は避けることができました。
コードは以下の通り単純です。

UIControl().sendAction(#selector(URLSessionTask.suspend), to: UIApplication.shared, for: nil)

この処理の後に先程のexit(0)を呼べば良いのですが以下のように直後に呼んでしまうと一瞬画面がブラックアウトしたように見えてしまい、これもまた好ましい挙動ではありません。

UIControl().sendAction(#selector(URLSessionTask.suspend), to: UIApplication.shared, for: nil)
exit(0)

よって、タイマーを用いて、ほんの少しの間遅延させて実行することとします。
自分は以下のように実装しました。

完成したコード

UIControl().sendAction(#selector(URLSessionTask.suspend), to: UIApplication.shared, for: nil)
Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false) { _ in
            exit(0)
        }

最後に

これからちょくちょく記事を書いて行くのでよかったら見て行ってもらえたら嬉しいです。
何かご指摘や質問(わからないかもですが)などありましたら気軽にコメントでもDMでもください。

追記

AppStore公開時の審査について

StackOverflowなどでこの方法では審査に通らないとする意見も見られるそうです。
しかし自分で調べたところ大手アプリにおいてもこの方法を使用しているものが見受けられたので、一概に落とされるとも限りません。
もし、この方法を用いたアプリを審査申請された方がおられましたらどうなったかコメント頂けると助かります。自分も次のアプリ公開時にこの方法を使用して試してみようと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

新 App Store 審査ガイドライン 翻訳&差分ガイド 2020年9月号

はじめに

これは2020年9月11日付けで変更された App Store 審査ガイドラインの翻訳&差分ガイドです。

前回からの主な変更点は、iOS 14 の App Clips とストリーミングゲームの取り扱いについてです。

ストリーミングゲームは、これまでに比べて緩和される方向性ではありますが、コレジャナイ感が凄い。ゲームタイトルごとに個別アプリとして申請、カタログアプリ内からは App Store へのリンクのみ許可など、ユーザ体験的にも残念な制約です。App Store のビジネスモデルを崩さずに妥協できるラインがこの辺りなのかとは思いますが。

end-users から end users への変更など、軽微な変更は修正項目から除外しています。

https://developer.apple.com/app-store/review/guidelines/

新規項目

2.5 Software Requirements

2.5.16 App Clips, Widgets, App Extensions, Notifications はアプリ機能に適している必要有。App Clips の機能はメインアプリにも含める必要有。App Clips での広告利用は不可。

App Clips, widgets, extensions, and notifications should be related to the content and functionality of your app. Additionally, all App Clip features and functionality must be included in the main app binary. App Clips cannot contain advertising.

App Clips だけの機能や広告は駄目。

3.1 Payments

3.1.2(a) Permissible uses:

● Apple が承認済みの音楽と映像のサブスクリプションアプリは携帯電話会社のバンドルに含まれる可能性有。

Apps that offer auto-renewing music and video subscriptions with prior approval by Apple may also be included in pre-defined bundles with cellular data plans offered in cellular carrier apps.

具体例は不明。

3.1.3(c) Enterprise Services: 組織向けの直販アプリではアプリ内課金以外の購入方法を追加可能。

If your app is only sold directly by you to organizations or groups for their employees or students (for example professional databases and classroom management tools), you may use purchase methods in addition to in-app purchase to collect those payments. Consumer, single user, or family sales must use in-app purchase.

一般消費者向けは変わらず。

3.1.3(d) Person-to-Person Experiences: 一対一の対人体験アプリではアプリ内課金以外の購入方法を追加可能。一対多は不可。

If your app enables the purchase of realtime person-to-person experiences between two individuals (for example tutoring students, medical consultations, real estate tours, or fitness training), you may use purchase methods other than in-app purchase to collect those payments. One-to-few and one-to-many realtime experiences must use in-app purchase.

家庭教師や医療相談などは Apple 税無しに。

3.1.3(e) Goods and Services Outside of the App: 物品や外部サービスの支払いはアプリ内課金の適用外。

If your app enables people to purchase physical goods or services that will be consumed outside of the app, you must use purchase methods other than in-app purchase to collect those payments, such as Apple Pay or traditional credit card entry.

内容は旧 3.1.5(a) と同等で、移行しただけ。

3.1.3(f) Free Stand-alone Apps: 有料ウェブツール向けのアプリではアプリ内で購入が発生しない限りアプリ内課金を実装する必要無し。

Free apps acting as a stand-alone companion to a paid web based tool (eg. VOIP, Cloud Storage, Email Services, Web Hosting) do not need to use in-app purchase, provided there is no purchasing inside the app, or calls to action for purchase outside of the app.

これは直近の WordPress 騒動への対応っぽい。

3.2 Other Business Model Issues

3.2.2(x) 個人向けローンを提供するアプリは条件を明示する必要有。手数料込みで APR 36% を超える請求は不可。60 日以内の全額返済請求は不可。

Apps offering personal loans must clearly and conspicuously disclose all loan terms, including but not limited to equivalent maximum Annual Percentage Rate (APR) and payment due date. Apps may not charge a maximum APR higher than 36%, including costs and fees, and may not require repayment in full in 60 days or less.

APR は年率のこと。

4.9 Streaming games

ストリーミングゲームは条件付きで許可。

Streaming games are permitted so long as they adhere to all guidelines — for example, each game update must be submitted for review, developers must provide appropriate metadata for search, games must use in-app purchase to unlock features or functionality, etc. Of course, there is always the open Internet and web browser apps to reach all users outside of the App Store.

4.9.1 各ストリーミングゲームは個別のアプリとして App Store に登録する必要有。

Each streaming game must be submitted to the App Store as an individual app so that it has an App Store product page, appears in charts and search, has user ratings and review, can be managed with ScreenTime and other parental control apps, appears on the user’s device, etc.

審査の都合上。

4.9.2 ストリーミングゲームサービスは App Store でカタログアプリを提供可能。サブスクリプションのアプリ内課金と Sign in with Apple を実装する必要有。カタログ内の全てのゲームは App Store の各製品ページへリンクする必要有。

Streaming game services may offer a catalog app on the App Store to help users sign up for the service and find the games on the App Store, provided that the app adheres to all guidelines, including offering users the option to pay for a subscription with in-app purchase and use Sign in with Apple. All the games included in the catalog app must link to an individual App Store product page.

カタログ機能は緩和されたが、ストア機能は App Store 以外許さない。

修正項目

2.3 Accurate Metadata

2.3.1 隠し機能は不可。Notes for Review で具体的に説明。機能しないコンテンツは不可。悪質な違反は開発者登録を抹消。

Don’t include any hidden, dormant, or undocumented features in your app; your app’s functionality should be clear to end users and App Review. All new features, functionality, and product changes must be described with specificity in the Notes for Review section of App Store Connect (generic descriptions will be rejected) and accessible for review. Similarly, you should not market your app on the App Store or offline as including content or services that it does not actually offer (e.g. iOS-based virus and malware scanners). Egregious or repeated behavior is grounds for removal from the Developer Program. We work hard to make the App Store a trustworthy ecosystem and expect our app developers to follow suit; if you’re dishonest, we don’t want to do business with you.

dormant を追記。

All new features, functionality, and product changes must be described with specificity in the Notes for Review section of App Store Connect (generic descriptions will be rejected) and accessible for review. を追記。

今後は Notes for Review も適切に書かないとリジェクト対象。

2.3.7 ユニークなアプリ名と正確なキーワードを指定。アプリ名は 30 文字まで。名前以外の記述を含めることは不可。不適切なキーワードは Apple により変更。

Choose a unique app name, assign keywords that accurately describe your app, and don’t try to pack any of your metadata with trademarked terms, popular app names, pricing information, or other irrelevant phrases just to game the system. App names must be limited to 30 characters and should not include prices, terms, or descriptions that are not the name of the app. App subtitles are a great way to provide additional context for your app; they must follow our standard metadata rules and should not include inappropriate content, reference other apps, or make unverifiable product claims. Apple may modify inappropriate keywords at any time or take other appropriate steps to prevent abuse.

pricing information を追記。

「基本プレイ無料」なども駄目かも?

3.1 Payments

3.1.2(a) Permissible uses:

● サブスクリプションは自身のアプリ間で共有可能。他者共有は不可。ユーザのすべてのデバイスで利用できる必要有。各ストリーミングゲームは App Store からダウンロードする必要有。

You may offer a single subscription that is shared across your own apps and services. Games offered in a streaming game service subscription must be downloaded directly from the App Store, must be designed to avoid duplicate payment by a subscriber, and should not disadvantage non-subscriber customers.

but these subscriptions may not extend to third-party apps or services. Games offered in a game subscription must be owned or exclusively licensed by the developer (e.g. not part of a game publishing platform). Each game から Games offered in a streaming game service subscription must be downloaded directly from the App Store に変更。

3.1.3 Other Purchase Methods: アプリ内課金を阻害するような施策は不可。

The following apps may use purchase methods other than in-app purchase. Apps in this section cannot, either within the app or through communications sent to points of contact obtained from account registration within the app (like email or text), encourage users to use a purchasing method other than in-app purchase.

3.1.3(a) “Reader” Apps: 課金導線がなければ購入済みコンテンツの利用は可能。リーダーアプリは無料アカウントの作成と管理機能が必要。

Apps may allow a user to access previously purchased content or content subscriptions (specifically: magazines, newspapers, books, audio, music, and video). Reader apps may offer account creation for free tiers, and account management functionality for existing customers.

Reader apps may offer account creation for free tiers, and account management functionality for existing customers. を追記。

アカウントの作成と管理機能を必須に変更。アプリから離脱せずに無料で試せないと駄目。

3.1.3(b) Multiplatform Services: マルチプラットフォーム展開のアプリは他所で購入したコンテンツの利用を許可。

Apps that operate across multiple platforms may allow users to access content, subscriptions, or features they have acquired in your app on other platforms or your web site, including consumable items in multi-platform games, provided those items are also available as in-app purchases within the app.

You must not directly or indirectly target iOS users to use a purchasing method other than in-app purchase, and your general communications about other purchasing methods must not discourage use of in-app purchase. を削除。同等の内容を 3.1.3 の冒頭に移行。

3.1.5 Cryptocurrencies:

旧 3.1.5(a) を 3.1.3(e) に移行。旧 3.1.5(b) を 3.1.5 に変更。

3.1.7 Advertising: 広告はメインアプリ内に限定し App Clips などで表示は不可。表示する広告はアプリのレーティングに準拠。アプリ内でターゲティング情報を開示。ユーザの機密データを利用した広告は不可。ユーザ体験を妨げる広告には広告明記と閉じるボタンが必須。

Display advertising should be limited to your main app executable, and should not be included in extensions, App Clips, widgets, notifications, keyboards, watchOS apps, etc. Ads displayed in an app must be appropriate for the app’s age rating, allow the user to see all information used to target them for that ad (without requiring the user to leave the app), and may not engage in targeted or behavioral advertising based on sensitive user data such as health/medical data (e.g. from the HealthKit APIs), school and classroom data (e.g. from ClassKit), or from kids (e.g. from apps in the Kids Category), etc. Interstitial ads or ads that interrupt or block the user experience must clearly indicate that they are an ad, must not manipulate or trick users into tapping into them, and must provide easily accessible and visible close/skip buttons large enough for people to easily dismiss the ad.

Display advertising should be limited to your main app executable, and should not be included in extensions, App Clips, widgets, notifications, keyboards, watchOS apps, etc. を追記。

以前から App Extensions などで広告は不可なので、App Clips も含めて明記しただけ。

3.2 Other Business Model Issues

3.2.2(vi) 使用条件の付加。レビューや他アプリのダウンロードの強要。

Apps should allow a user to get what they’ve paid for without performing additional tasks, such as posting on social media, uploading contacts, checking in to the app a certain number of times, etc. Apps should not require users to rate the app, review the app, watch videos, download other apps, tap on advertisements, enable tracking, or take other similar actions in order to access functionality, content, use the app, or receive monetary or other compensation, including but not limited to gift cards and codes.

enable tracking を追記。

4.5 Apple Sites and Services

4.5.2(i) MusicKit API を使うアプリにはメディア制御機能が必要。Apple Music を利用した収益化は不可。Music Kit ドキュメントで許可されていない音源共有は不可。

MusicKit on iOS lets users play Apple Music and their local music library natively from your apps and games. When a user provides permission to their Apple Music account, your app can create playlists, add songs to their library, and play any of the millions of songs in the Apple Music catalog. Users must initiate the playback of an Apple Music stream and be able to navigate using standard media controls such as “play,” “pause,” and “skip.” Moreover, your app may not require payment or indirectly monetize access to the Apple Music service (e.g. in-app purchase, advertising, requesting user info, etc.). Do not download, upload, or enable sharing of music files sourced from the MusicKit APIs, except as explicitly permitted in MusicKit documentation.

前半の文章を改変しただけ。

5.1 Privacy

5.1.2(vi) HomeKit API, HealthKit, Consumer Health Records API, MovementDisorder API, ClassKit などから収集したデータのマーケティング利用は不可。

Data gathered from the HomeKit API, HealthKit, Clinical Health Records API, MovementDisorder APIs, ClassKit or from depth and/or facial mapping tools (e.g. ARKit, Camera APIs, or Photo APIs) may not be used for marketing, advertising or use-based data mining, including by third parties. Learn more about best practices for implementing CallKit, HealthKit, ClassKit, and ARKit.

Consumer Health Records API から Clinical Health Records API に変更。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む