Xcode + AppleScriptを使ったiPhone位置偽装 (円形歩行器)【備忘録】

# 目的
○ク三ンブルームのビッグフラワーの周りをぐるぐる歩かせたくなったので、円形歩行器を作りました。
**ビッグフラワーの周りに花を植えるだけで歩数はカウントされません。**
（ビッグフラワーを咲かせるのが大変な田舎での利用を想定しています。）
家の近くの座標であれば急激な座標変化を伴わないのでBANされづらいかもしれません。
ポ◯モンGOに流用するとより威力を発揮すると思います。
詳しいBAN要件については以下の記事を参照ください。


[【ポケモンGO】Niantic(ナイアンティック)の「不正行為に関する 3 ストライクポリシー」
](https://pokemongo-get.com/pokego03735/)

```
「特定のGPS座標から特定のGPS座標までの距離を、通常ではありえない時間内で移動する」という、
いわゆる「テレポート」に関する不正検知はしっかりしているようですが、
位置偽装アプリを使用して「常に特定のGPS座標に遠隔ログインする」という不正行為の検知には至っていない様子です。
```

この記述を読む限りは今回の円形歩行器はNianticのBAN検知に耐性があるようです。




# 参考文献
Xcode + AppleScriptはこちらの記事を参考にしました。
細かいファイルの置き場所がわからなくなったら参照してください。
[iPhoneの実機で位置情報をシミュレーションする方法](https://qiita.com/narikei/items/c20d95532130e8fcdeb7)

AppleScriptで三角関数を実装する方法
[三角関数をなんとか使いたい　[AppleScript辞書はつくれるか？]](https://start-mac.blog.ss-blog.jp/2017-07-31)

AppleScript内からJavaScriptって呼び出せるんですね。。無限の可能性を感じました。


# 実行環境
- iOS 14.5
- mac OS Big Sur(11.2.3)
- Xcode 12.5.1
- Macbook air (m1 2020) メモリ16GB

~~記事を書いてる時点では(2021/11/09)、iOS15.1にXcodeおよびXcode betaが対応していない可能性があります。~~
Xcode bataを使うことでiOS 15.1でも実行できました。


# 概要

位置偽装用のダミーアプリを作成しバッググランド処理で起動を継続
↓
緯度経度を記載したGPXファイルをXcodeのプロジェクトファイルに追加
↓
apple scriptで毎秒GPXファイルを書き換え

# ソースコードと手順

XcodeでiOSアプリの新規プロジェクトを作成する。

![スクリーンショット 2021-11-09 14.31.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331368/59063ac0-d3b7-aadf-aef9-653c086ad85d.png)

__swiftUIではなくstoryboardを選択しないとAppDelegateが生成されないので注意__
![スクリーンショット 2021-11-09 14.58.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331368/5be1bbbf-a319-4ddc-6c3b-4d812bc8ae50.png)

AppDelegate.swiftにバックグラウンド処理を記述する。
AppDelegateに必要なメソッドは下記のみでありそのほかの元からあるメソッドは削除しても動く


```swift:AppDelegate.swift
//
//  AppDelegate.swift
//  Auto walker
import UIKit
import CoreData

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    var backgroundTaskID: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier(rawValue: 0)

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        return true
    }

    func applicationWillResignActive(application: UIApplication) {
        self.backgroundTaskID = application.beginBackgroundTask(){
            [weak self] in
            application.endBackgroundTask((self?.backgroundTaskID)!)
            self?.backgroundTaskID = UIBackgroundTaskIdentifier.invalid
        }
    }
}

```

MyLocation.gpxをXcodeプロジェクトに追加
初期位置は自動で書き込まれるため、ファイルには何も書かない
すでに何か書き込まれていると正しく動作しない場合が多い

```tex:MyLocation.py

```

applescriptに円運動と位置更新のコードを書く


```applescript:auto_circular_motion.scpt
-- 初期位置から円運動させる
-- 初期位置はGoogle Mapで適当にピンを刺し緯度経度を取ってくる
-- display dialog {変数} でデバッグできる

set GPX_FILE to "~~~~~~~~~~~~" -- gpxファイルのパス
set CENTER_LAT to 35.68123601 -- 初期緯度 少数第8位まで桁を揃えないとバグるので注意
set CENTER_LON to 139.76712501 -- 初期経度
set LAT_PER_M to 8.98314 * 1.0E-6 -- 1mあたりの緯度
set LON_PER_M to 1.09663 * 1.0E-5 -- 1mあたりの経度
set ROUND_N to 10 ^ 7 -- 切り捨て計算の定数

set r to 45.0 -- 円運動の半径(m)
set a to 0 -- 初期角度(度)
set s to 1.0 -- 歩行速度(m/s)
set da to 360 / (r * 2 * 3.14 / s) -- 角速度(度/s) 360/一周にかかる時間


-- 初期位置をgpxファイルに書き込む
set outputStr to "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
set outputStr to outputStr & "<gpx version=\"1.1\">"
set outputStr to outputStr & "<wpt lat=\"" & CENTER_LAT & "\" lon=\"" & CENTER_LON & "\" />"
set outputStr to outputStr & "</gpx>"


open for access POSIX file GPX_FILE with write permission
write outputStr to POSIX file GPX_FILE as «class utf8»
close access POSIX file GPX_FILE

-- Xcodeを選択しシュミレータをクリックする
tell application "System Events" to tell process "Xcode"
	click menu item "MyLocation" of menu 1 of menu item "Simulate Location" of menu 1 of menu bar item "Debug" of menu bar 1
end tell

-- 毎秒gpxファイルを書き換える
repeat
	set jsText to "r = " & r & "; a = " & a & ";

	x = r * Math.cos(a / 180 * Math.PI); y = r * Math.sin(a / 180 * Math.PI);

	[x,y]; //結果をApplescriptに返す"
	set {x, y} to run script jsText in "Javascript"
	set lat to CENTER_LAT + LAT_PER_M * x
	set lon to CENTER_LON + LON_PER_M * y
	
	-- 桁数が変わるとgpxファイルがバグるので小数第7位で切り捨て、8位に1を足す
	set lat to lat * ROUND_N
	set lat to round lat rounding down
	set lat to lat / ROUND_N + 1.0E-8
	set lon to lon * ROUND_N
	set lon to round lon
	set lon to lon / ROUND_N + 1.0E-8
	
	set outputStr to "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
	set outputStr to outputStr & "<gpx version=\"1.1\">"
	set outputStr to outputStr & "<wpt lat=\"" & lat & "\" lon=\"" & lon & "\" />"
	set outputStr to outputStr & "</gpx>"
	
	open for access POSIX file GPX_FILE with write permission
	write outputStr to POSIX file GPX_FILE as «class utf8»
	close access POSIX file GPX_FILE
	
	tell application "System Events" to tell process "Xcode"
		click menu item "MyLocation" of menu 1 of menu item "Simulate Location" of menu 1 of menu bar item "Debug" of menu bar 1
	end tell
	
	set a to a + da
	delay 1
end repeat
```

# 使用イメージ
以下のように最高効率でビッグフラワーを咲かせることが目的です。
![IMG_3490.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331368/5e7e6355-7189-3ec0-762e-8f5543489a52.jpeg)


# あとがき
今回のコードのポイントは緯度経度の桁数を揃えることです。理由は不明ですが、桁数が変わるとGPXファイルの記述が乱れます。
モバイル開発経験は一切ないのでバックグラウンド処理に関する理解は曖昧です。
そのためピクミンブルーム側でリソースを多く使う処理をするとバックグラウンドアプリが落ちます。