備忘録　Swift / Xcode / Mac

[Swift編](#swift%E7%B7%A8)

[Xcode編](#xcode%E7%B7%A8)

[Mac編](#mac%E7%B7%A8)

[その他](#%E3%81%9D%E3%81%AE%E4%BB%96)


---------------------------------------

## Swift編

### ◇ dateのJST表記

```swift
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "ja_JP")
dateFormatter.timeZone = TimeZone(identifier: "Asia/Tokyo")
dateFormatter.dateFormat = "yyyy.M.d(E)H:mm:ss"
let jstText = dateFormatter.string(from: Date()) // 2018.8.29(水)21:58:12

//HH:mm:ss.SSS   SSS:ミリ秒
```

```swift:extension版
extension Date {
    func dateFormatter(format: String, local: String? = "ja_JP", timeZone: String? = "Asia/Tokyo") -> String {
        let dateFormatter = DateFormatter()
        if let local {
            dateFormatter.locale = Locale(identifier: local)
        }
        if let timeZone {
            dateFormatter.timeZone = TimeZone(identifier: timeZone)
        }
        dateFormatter.dateFormat = format
        return dateFormatter.string(from: self)
    }
} 

//let d = Date.now.dateFormatter(format: "yyyy.M.d(E)H:mm:ss")
```


### ◇ 非同期処理

```swift
DispatchQueue.global(qos: .default).async {
  // サブスレッド(バックグラウンド)で実行する処理

    DispatchQueue.main.async {
        // メインスレッドで実行する処理

    }
}
```

### ◇ 遅延処理（2パターン）

```swift
//DispatchQueue
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
    print("1.5秒後の処理")
}

//Timer
Timer.scheduledTimer(withTimeInterval: 1.5, repeats: false) { _ in
    print("1.5秒後の処理")
}
```

### ◇ NSData

```swift
// swiftの配列からNSDataへ変換
var rawArray:[UInt8] = [0x01, 0x02, 0x03]
let data = NSData(bytes: &rawArray, length: rawArray.count)
print(data)

// バイト列からNSDataに変換
var buffer = [UInt8](count: 3, repeatedValue: 0x00)
data.getBytes(&buffer, length: 3)
print(buffer)
```

```swift
// Byte配列 → Data
let data = Data(bytes: bytes)

// Data → Byte配列
let bytes = [Byte](data)
```



### ◇ String系

Swiftのバージョンによって随分と表記が異なります。Qiita上でも2.xなど古い情報が多い。
そんな中で、v5のStringについて書いてあるサイトはここ。
[【Swift5】文字列操作(String型)チートシート〜置換,結合,削除,比較,取得〜](https://satoriku.com/string-cheatsheet/)

[【Swift4】文字列（NSString）の置換・分割・検索（完全一致、前方一致、後方一致、部分一致）](http://program-life.com/380)

### ◇ @availableアノテーション

@availableアノテーションは、funcやclass、struct、enumには指定できるが、Storedプロパティには指定できない。そんな時は`lazy`をつけて遅延Storedプロパティにしてあげると指定できるようになる。

```swift：エラーになる
@available(iOS 14.0, *)
var key0 = GCKeyCode.zero
```

```swift：エラーにならない
@available(iOS 14.0, *)
lazy var key = GCKeyCode.zero
```

## Xcode編

### ◇ コンパイルが遅いとき

以下のフラグを`Swift Compiler - Custom Flags`に設定する

関数ボディ：1000ms、式の評価：100msを超えるとワーニングが出る様になる。

```
-Xfrontend -warn-long-function-bodies=1000 -Xfrontend -warn-long-expression-type-checking=100
```

![s1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/d24940ab-c3b0-59b1-b6d1-07aa82113f8b.png)


### ◇ Xcode.appのキャッシュ削除

```
rm -rf ~/Library/Caches/com.apple.dt.Xcode/
```

### ◇ Xcodeプロジェクトのキャッシュ削除

```
rm -rf ~/Library/Developer/Xcode/DerivedData
```

### ◇ Xcodeプレビューデータの削除

```
xcrun simctl --set previews delete all
または
rm -rf ~/Library/Developer/Xcode/UserData/Previews
```

### ◇ 不要スペース検索（正規表現）

```:行末
\s+$
```

```:空白行
^\s+$
```

```:カンマの後ろにスペースを1個空ける
([^ ]),([^ ])
$1, $2
```

### ◇ ショートカットキーの追加

`IDETextKeyBindingSet.plist` ファイルの最下部に追加（XMLの閉タグの整合性に注意）
Xcodeのメニューから、Preferences... → Key Bindings タブに進み、このコマンドを選択して、お好きなショートカットキーを割り当る。

```sh
sudo vi /Applications/Xcode.app/Contents/Frameworks/IDEKit.framework/Versions/A/Resources/IDETextKeyBindingSet.plist
```

```xml:行追加
    <key>Custom Key Bind</key>
        <dict>
        <key>Continue Newline (Custom)</key>
        <string>moveToEndOfLine:, insertNewline:</string>
        </dict>
```

### ◇ Userdefaultsの保存先

- iOS

[NSUserDefaultsの保存場所](https://qiita.com/dondoko-susumu/items/85f5e1449df94be20fa9)


- Mac Catalyst

```
> defaults read com.nak435.TimeCalc
{
    "NSWindow Frame MainSceneWindow" = "112 170 514 602 0 0 1440 875 ";
    history =     (
    );
    startupCount = 18;
}
> defaults delete com.nak435.TimeCalc
> defaults read com.nak435.TimeCalc
defaults[1641:89385] Domain /Users/USERNAME/Library/Containers/com.nak435.TimeCalc/Data/Library/Preferences/com.nak435.TimeCalc does not exist
>
```


参考：[nsuserdefaultsのクリア](https://kisagai.com/2012/02/13/nsuserdefaultsのクリア/)



### ◇ コンパイル時のMac Catalystの判定

```swift
#if targetEnvironment(macCatalyst)

#endif
```


### ◇ コンパイル時（実行時）のプラットホームの判定

```swift
var isPad: Bool { UIDevice.current.userInterfaceIdiom == .pad }

var isPreview: Bool { ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" }

enum platform {
    case none, iPhone, iPad, mac //macOS, macCatalyst
}

var thisPlatform: platform = .none
#if os(macOS)
    thisPlatform = .mac  //macOS
#elseif targetEnvironment(macCatalyst)
    thisPlatform = .mac  //macCatalyst
#elseif os(iOS)
    thisPlatform = isPad ? .iPad : .iPhone
#endif
```

macOSとmacCatalystの区別も可能




### ◇ 実行時にシステムバージョンの取得

```swift
//for iOS
var systemVersion = UIDevice.current.systemVersion
//String("16.4")

//for macOS
var systemVersion = ProcessInfo.processInfo.operatingSystemVersion
//NSOperatingSystemVersion(majorVersion: 13, minorVersion: 4, patchVersion: 1)
```




### ◇ ダークモード（Dark mode）の判定

```swift
// UIKit
var isDarkMode = UITraitCollection.current.userInterfaceStyle == .dark
```

```swift
// SwiftUI
struct SampleView: View {
    @Environment(\.colorScheme) var colorScheme
    var isDarkMode: Bool { colorScheme == .dark }
}
```

### ◇ error: root manifest not found

`swift build`や`swift package generate-xcodeproj`で`root manifest not found`エラーが出た時は、その前に、`swift package init --type library`を実行すること。

```sh
swift package init --type library
swift build
swift package generate-xcodeproj
```


### ◇ iOSシミュレータの画面録画

```shell:Terminal
# カレントディレクトリにファイルが作成される。⌃Cで停止
xcrun simctl io booted recordVideo output.mov
```


## Mac編

### ◇ スクリーンショットの余白を無くす

```shell
defaults write com.apple.screencapture "disable-shadow" -bool "false"
```



### ◇ APPプレビュー動画の作成手順

```shell:Terminal
# Step.1
# iOSシミュレータ画面の録画(H264)　（Macの場合はQuickTimePlayerでスクリーン録画する）
xcrun simctl io booted recordVideo output.mov --codec=h264

# Step.2
#サイズの変更（アスペクト比は下表参照）
ffmpeg -i input.mov -s 1080x1920 -aspect 9:16 -pix_fmt yuv420p output.mov

# Step.3
#30秒以内にカット
いろいろ（トリミングや切り出し）

# Step.4
#30fpsに統一
ffmpeg -i output.mov -filter:v fps=fps=30 output_f30.mov

# Step.5
#無音オーディオ追加
ffmpeg -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 -i output_f30.mov -shortest -c:v copy -c:a aac output_aac.mov
```

アスペクト比（-aspect）の設定例

| デバイス | w x h | aspect |
|:-:|:-:|:-:|
| iPhone 6.5inch 縦  | 886x1920  | 6:13  |
| iPhone 5.5inch 縦  | 1080x1920  |  9:16 |
| iPad 12.9inch  縦  | 1200x1600  | 3:4 |
| Mac  | 1920x1080  | 16:9  |


[APPプレビューの仕様](https://help.apple.com/app-store-connect/?lang=ja-jp#/dev4e413fcb8)


### ◇ スクリーンショットを撮る方法

→「shift」＋「command」＋「5」
[Mac でスクリーンショットを撮る方法](https://support.apple.com/ja-jp/HT201361)


### ◇ ローカルGit

- 準備

参考：[Git の基礎勉強 〜Xcodeから、Gitによるバージョン管理を使う〜](https://tracpath.com/bootcamp/learning_git_with_xcode.html)

```Terminal
git config -–global user.email "app@nak435.com"
git config -–global user.name "USERNAME"

git config -–global core.quotepath false

git config -–global core.autocrlf false

git config -–global push.default upstream

git config --list
```

参考：[XcodeでGitを使う方法](https://naoya-ono.com/swift/xcode-git/)


### ◇ 音量平準化

参考：[MacでMP4の音量を調整(統一)する](http://mwtndmik.hatenablog.com/entry/2016/08/01/124453)

```Terminal
aacgain -r -c -p -d 0 *.m4a
```

### ◇ mp4からサウンドを抽出

Mac標準で変換可能。
Finderでmp4ファイルを右クリック→「サービス」→「選択したビデオファイルをエンコード」
「メディアをエンコード」ダイアログにて「オーディオのみ」を選択。「続ける」をクリック。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/13bb1868-84f9-1db6-eec2-622eacf0be0f.png)
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/14d5085b-7e54-b287-9645-37e5aea5e823.png)


### ◇ m4aをmp3に変換

```Terminal
ffmpeg -i "元のファイル.m4a" -acodec libmp3lame -ab 128k "変換後ファイル.mp3"
```

ffmpegは`brew install ffmpeg`でインストール

`ffmpeg -hide_banner`

### ◇ movをアニメーションgifに変換


```Terminal
ffmpeg -i "元のファイル.mov" -r 30(レート) "変換後ファイル.gif"
```


### ◇ mp4を4倍速に変換

```Terminal
ffmpeg -i "元のファイル.mp4" -vf setpts=PTS/4.0 -af atempo=4.0 "変換後ファイル.mp4"
```


### ◇ movをmp4に変換

```Terminal
ffmpeg -i "元のファイル.mov" -crf 1 -pix_fmt yuv420p "変換後ファイル.mp4"

#Macでは再生できない
ffmpeg -i "元のファイル.mov" "変換後ファイル.mp4" 
```


### ◇ mp4をmovに変換

```Terminal
ffmpeg -i "元のファイル.mp4" -f mov "変換後ファイル.mov"
```


### ◇ mp4をx倍速に変換

```Terminal
#1.5倍速
ffmpeg -i "元のファイル.mp4" -vf setpts=PTS/1.5 "変換後ファイル.mp4"

#0.5(1/2)倍速（ゆっくり）
ffmpeg -i "元のファイル.mp4" -vf setpts=PTS/0.5 "変換後ファイル.mp4"
```



### SVGをPNGに変換（Inkscapeをバッチ使用）


```sh:aaa.svgをbbb.pngに変換
/Applications/Inkscape.app/Contents/MacOS/inkscape -b white -w 1024 -p aaa.svg -o bbb.png
```

**オプションの説明**

**-b** 背景色の指定。指定しないと透明になる
**-w** イメージのサイズ(幅をピクセル値で指定。-w省略可能）
**-p** 入力ファイル(svg)
**-o** 出力ファイル(png)


### ◇ 動画関係

```shell:動画関係
#動画サイズを変更
ffmpeg -i input.mov -s 1080x1920 -pix_fmt yuv420p output.mov

#開始10秒後から30秒を切り出し。-c copyオプションを省略すると処理時間がかかるが時間が正確。
ffmpeg -ss 10 -i input.mov -t 30 [-c copy] output.mov
```



```shell:その他の使い方
# ffmpegを使って静止画と音源ファイルから動画を作る
# 最初に音源ファイルの長さ（+1秒くらいのバッファ持たせたほうがいい）の動画を作る。
ffmpeg -f image2 -r 1 -loop_input -t 動画の秒数 -i img.jpg empty.avi

# 次に動画と音源ファイルを組み合わせて動画出力。
ffmpeg -i empty.avi -i sound.mp3 out.avi



# 入力動画の情報を見る（出力ファイルが指定されていない旨のエラーは表示される）
ffmpeg -i example.avi


# 複数の静止画からの動画作成（pic00001.png、pic00002.png、pic00003.png、。。。）
ffmpeg  -i pic%05d.png movie.mp4

# 動画の結合（%03dは使えない）
ffmpeg -i no1.avi -i no2.avi -filter_complex "concat=n=2:v=1:a=1" ouput.mp4 


# 4fps (1コマ0.25秒)
ffmpeg -r 4 -i /Volumes/AS30V/DCIM/100MSDCF/%05d.JPG -vcodec mjpeg -qscale:v 0 bbb.avi


# オーディオの削除（無音動画になる）
ffmpeg -I input.mov -vcodec copy -an output.mov
```

### ◇ キーボードで打てない文字たち

```
⌘ ： command
⌃ ： control
⌥ ： option
⇧ ： shift
```


```
© ： ⌥G
® ： ⌥R
™ ： ⌥2
```

```
⚠️
```

### ◇ Serial通信

ArduinoのSerialをMacで受ける場合、Macのターミナルで`screen`コマンドを打つ。

```shell
screen /dev/tty.usbmodem14401 115200
```

シリアルポートは、`ls /dev/tty.*`で確認する。
通信スピードは、Arduino側のスケッチで指定した値と一致させること。


`Ctrl+A d`でscreenを終わる(detach)。
直前のセッションに再び接続するには`screen -R`。

終了は`Ctrl+A Ctrl+A y`でscreenを終わる。


```cpp
void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.print("millis: ");
  Serial.println(millis());
  delay(1000);
}
```


### ◇ ターミナルのログ出力

scriptコマンドで出力ファイルを指定。新たなシェル内が出力対象。Ctrl+Dで終了。

```shell
script -a -F logfile.txt
(Ctrl+D)
```

### ◇ Automatorのコマンドライン起動

```shell
autometor [-i [-|inputFile] ] file.workflow
```

引数が不要であれば-iは不要。-は標準入力から与える。


### ◇ ファイル比較

むかしWindowsSDKにあった`WinDiff`に相当するもの。

GUI版 `FileMerge`（Spotlightで検索）

コマンドライン `opendiff file1 file 2`

参考URL：<https://reasonable-code.com/filemerge/>



### ◇ TerminalをX86_64モードにする

M1 Macを使っている場合、TerminalでRosettaを使いX86_64モードにする

```shell
uname -m
arm64   # Apple silicon

arch -x86_64 zsh
uname -m
x86_64   # Intel architecture
```



### ◇ BOM付きUTF-8へ上書き

```shell
brew install nkf  #インストール

nkf —overwrite —oc=UTF-8-BOM file.csv
```


### ◇ grep 再帰

```shell
cd path

#case ignore
grep -i "キーワード" 

#ファイル名のみ
grep -lr "キーワード" --include='*.ext' .

#ファイル名＋行番号＋内容
grep -nr "キーワード" [--include='*.ext'] .
```


### ◇ telnetをcurlで代替

macOSのデフォルト環境においてtelnetはインストールされない。curlで代替する。

```shell
% curl -v telnet://<host>:<port>

% curl -v http://<host>:<port>
```


### ◇ ユニバーサルクリップボードが使えない場合

#### まずは以下を試す

5. iPhone / Mac 再起動
4. iCloud Sign Off / Sign In
3. Wifi Off / On（同じSSID）
2. Bluetooth Off / On
1. Handoffの使用許可 Off / On

#### それでもダメな場合は以下のコマンドをターミナルで叩く

```terminal
defaults write ~/Library/Preferences/com.apple.coreservices.useractivityd.plist ClipboardSharingEnabled 1
```




### ◇ ディスク占有の（大きい）フォルダ検索

```terminal
sudo du --si -xct 1000000000 /System/Volumes/Data | tee >(grep -ve $'*	' > ~/Downloads/DiskSpace.txt) | sort -nr > ~/Downloads/SortedDiskSpace.txt
```





## その他

### ◇ Naming rule

```console
キャメルケース ( camelCase  )
パスカルケース ( PascalCase )
スネークケース ( snake_case )
ケバブケース   ( kebab-case )
```

https://www.wakuwakubank.com/posts/804-it-naming-convention/



### ◇ Blu-Ray to mp4

1. VCL convert mp4 -> input.mp4 (Video)
1. VCL convert mp3 -> input.mp3 (Audio)
1. QTPlayer imput.mp4 remove audio -> input2.mp4
1. ffmpeg input2.mp4 + input.mp3 -> output.mp4
`ffmpeg -i input2.mp4 -i input.mp3 -c:v copy -c:a aac -map 0:v:0 -map 1:a:0 output.mp4`