Projucerを限界まで使い続ける人のためのTips

## We love Projucer! ##
ProjucerのGUI EditorはDeprecatedになってしまい、JUCE 8.0.1からはなくなってしまった。しかしJUCEを使う上でこんなに便利なRADツールは他にないので、限界まで使い続ける。

## 生成されたコードのJUCEバージョンチェックを外す ##
Version 7.0.12のProjucerを使ってVersion 8.x.xとか上のバージョンのJUCE modulesをリスクを承知の上で使う。そのまま使うとバージョンチェックが入ってビルドエラーで止まるので、このチェックを外す。下記のコードの#if、#error、#endifをこのようにコメントアウトするようにする。

```juce_ProjectSaver.cpp
    if (hasBinaryData && project.shouldIncludeBinaryInJuceHeader())
        out << CodeHelpers::createIncludeStatement (project.getBinaryDataHeaderFile(), getAppConfigFile()) << newLine;

    out << newLine
        << "//#if defined (JUCE_PROJUCER_VERSION) && JUCE_PROJUCER_VERSION < JUCE_VERSION" << newLine
        << " /** If you've hit this error then the version of the Projucer that was used to generate this project is" << newLine
        << "     older than the version of the JUCE modules being included. To fix this error, re-save your project" << newLine
        << "     using the latest version of the Projucer or, if you aren't using the Projucer to manage your project," << newLine
        << "     remove the JUCE_PROJUCER_VERSION define." << newLine
        << " */" << newLine
        << "// #error \"This project was last saved using an outdated version of the Projucer! Re-save this project with the latest version to fix this error.\"" << newLine
        << "//#endif" << newLine
        << newLine;

    if (project.shouldAddUsingNamespaceToJuceHeader())
// snip
```

## Font関連のWarning ##
ProjucerはComponentのコードに関してこんなコードを書いてくる。
```cpp
labelProgramName->setFont (juce::Font (17.00f, juce::Font::plain).withTypefaceStyle ("Regular"));
```
これはJUCE 8ではこうでなければならない。
```cpp
labelProgramName->setFont (juce::Font (juce::FontOptions (17.0f, juce::Font::plain)));
```
しかし、これはいくら手動で書き直しても、Projucerがコードを吐き出すたびに戻されてしまうので、Projucerを変更するしかない。

変更前
```jucer_FontPropertyComponent.h
    static String getCompleteFontCode (const Font& font, const String& typefaceName)
    {
        String s;

        s << "juce::Font ("
          << getTypefaceNameCode (typefaceName)
          << CodeHelpers::floatLiteral (font.getHeight(), 2)
          << ", ";

        if (font.getAvailableStyles().contains (font.getTypefaceStyle()))
            s << "juce::Font::plain).withTypefaceStyle ("
              << CodeHelpers::stringLiteral (font.getTypefaceStyle())
              << ")";
        else
            s << getFontStyleCode (font)
              << ")";

        if (! approximatelyEqual (font.getExtraKerningFactor(), 0.0f))
            s << ".withExtraKerningFactor ("
              << CodeHelpers::floatLiteral (font.getExtraKerningFactor(), 3)
              << ")";

        return s;
    }
```

変更後
```jucer_FontPropertyComponent.h
    static String getCompleteFontCode (const Font& font, const String& typefaceName)
    {
        String s;

        s << "juce::Font (juce::FontOptions ("
          << CodeHelpers::floatLiteral (font.getHeight(), 2)
          << ", ";

        // スタイルの判定
        if (typefaceName == "Bold Italic")
            s << "juce::Font::bold | juce::Font::italic";
        else if (typefaceName == "Bold")
            s << "juce::Font::bold";
        else if (typefaceName == "Italic")
            s << "juce::Font::italic";
        else
            s << "juce::Font::plain"; // "Regular" に相当

        s << "))";

        if (! approximatelyEqual (font.getExtraKerningFactor(), 0.0f))
            s << ".withExtraKerningFactor ("
              << CodeHelpers::floatLiteral (font.getExtraKerningFactor(), 3)
              << ")";

        return s;
    }
```

## Mac/iOSのPList変更 ##
Exporterの設定でCustom PListに下記のように書く。これの<dict>と</dict>の中身を必要に応じて足していく。下記のkeyとvalueはただの例。
```xml
<plist>
  <dict>
    <key>com.apple.security.application-groups</key>
    <array>
        <string>group.com.yourcompany.yourproduct</string>
    </array>
  </dict>
</plist>
```

## App Groups対応 ##
### iOSの場合 ###
ExporterのApp Grop CapabilityをEnabledにして、App Group IDにIDを記述。複数ある場合はセミコロンで区切る。

### Macの場合 ###
MacのExporterはApp Groupをサポートしてないので自力でやらねばならない。

まずPListに追加。
```xml
    <key>com.apple.security.application-groups</key>
    <array>
        <string>group.com.yourcompany.yourproduct</string>
    </array>
```
これだけだとMac/iOS共にCapabilityにApp Groupは足されるけど値にチェックが入らないので（なんでやねん！）、Entitlementsをビルド前に「必要に応じて」書き換える。このためにExporterのPre-Build Shell Scriptに下記を書く。ただしこれのせいでビルド中にEntitlementsが変更されるため、最初の１回だけビルドがエラーでストップしてしまう（運良くならないこともある）。VST、VST3、AudioUnit(v2)はそもそもApp Groupには対応できないのでこれは必要ない。
```sh
#!/bin/bash

# 対象となるエンタイトルメントファイルのリスト
ENTITLEMENTS_FILES=(
    "$PROJECT_DIR/AUv3_AppExtension.entitlements"
    "$PROJECT_DIR/Standalone_Plugin.entitlements"
)

# チェックするキーと追加する値
KEY_TO_CHECK="com.apple.security.application-groups"
APP_GROUP="group.tokyo.studio-r.ifw"

# 各エンタイトルメントファイルを処理
for ENTITLEMENTS_FILE in "${ENTITLEMENTS_FILES[@]}"; do
    echo "Processing ${ENTITLEMENTS_FILE}..."

    # ファイルが存在しない場合はエラーを出力してスキップ
    if [ ! -f "$ENTITLEMENTS_FILE" ]; then
        echo "Error: Entitlements file not found: $ENTITLEMENTS_FILE"
        continue
    fi

    # アプリグループの設定
    if ! /usr/libexec/PlistBuddy -c "Print ${KEY_TO_CHECK}" "$ENTITLEMENTS_FILE" &>/dev/null; then
        echo "Adding ${KEY_TO_CHECK} as a new array to ${ENTITLEMENTS_FILE}"
        /usr/libexec/PlistBuddy -c "Add ${KEY_TO_CHECK} array" "$ENTITLEMENTS_FILE"
    fi
    if ! /usr/libexec/PlistBuddy -c "Print ${KEY_TO_CHECK}" "$ENTITLEMENTS_FILE" | grep -q "$APP_GROUP"; then
        echo "Adding ${APP_GROUP} to ${KEY_TO_CHECK} in ${ENTITLEMENTS_FILE}"
        /usr/libexec/PlistBuddy -c "Add ${KEY_TO_CHECK}: string $APP_GROUP" "$ENTITLEMENTS_FILE"
    fi

    echo "Successfully updated ${ENTITLEMENTS_FILE}!"
done

echo "All entitlements files processed successfully!"

```


## iCloud Documents対応 ##
TBD.

## Supported Destinations ##
iPad用のアプリはデフォルトではMacとApple Visionで動作するように設定されるが、これを外したい場合は下記をiOS用Exporterで**Debug/Releaseの両方の**Custom Xcode Flagsに入れる。
```
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO, SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO
```

## dSYMを出力する ##
iOS、MacのExporterのRelease（もしくはDebug）のCustom Xcode Flagsに下記を追加する。
```
GCC_GENERATE_DEBUGGING_SYMBOLS=YES,GCC_DEBUGGING_SYMBOLS=full,DEBUG_INFORMATION_FORMAT=dwarf-with-dsym,COPY_PHASE_STRIP=NO
```
この設定でdSYMはビルドされた実行ファイルと同じ場所に作成される。しかし、Product -> Archiveでパッケージ化した場合にパッケージの中のdSYMフォルダにはコピーされない（やり方探したが見つからない）。なので、App Storeに提出の際にはパッケージを作成した後でパッケージを開いてdSYMフォルダにXXX.dSYMファイルを手動でコピーしてから提出する。

## iOSのアプリをMacとApple Vision非対応にする ##
iOSのExporterのCustom Xcode Flagsに下記を追加する。
```
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD=NO, SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD=NO
```