トップ 最新 追記

Cocoa練習帳

iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど

2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|

2021-01-02 [SwiftUI]Appプロトコル

XcodeでiOSの新規Appプロジェクトを生成すると雛形から作られるのが、Appプロトコルを実装する〜Appクラスだ。

import SwiftUI
 
@main
struct LandmarksApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

計算型プロパティbodyの実装は必須となる。

WindowGroupはビュー階層のコンテナ。

ContentViewはアプリで独自に実装したビュー。

import SwiftUI
 
struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
            .padding()
    }
}
 
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Viewプロトコルを実装したContentViewがアプリ独自のビュー。PreviewProviderプロトコルを実装したContentView_PreviewsはXcodeのプレビュー表示にContentViewを表示させるためのもの。

ContentViewの計算型プロパティbodyにビューの内容を実装する。

サンプルコードでは、文言"Hello, World!"をテキスト描画している。


2021-01-05 [Android][Gradle][ndk-build]既存のネイティブ・ソースをGradleに組み込む

ndk-buildを使っている既存のネイティブ・ソースをGradleに組み込み手順を調べた。

Android Studio UIを利用すると思わぬ副作用が発生する懸念があるのと、Android Studioの利用は次の段階で検討するので、手動で対応する方法のみとなっている。

gradle.propertiesにCMakeでなくndk-buildを選択していると設定する。

PROP_APP_ABI=armeabi-v7a:arm64-v8a
PROP_BUILD_TYPE=ndk-build

build.gradleのdefaultConfigブロックにndk-buildのオプションを設定する。

android {
  ...
  defaultConfig {
    ...
    externalNativeBuild {
      if (PROP_BUILD_TYPE == 'ndk-build') {
        ndkBuild {
          arguments '-j4'
          arguments 'NDK_MODULE_PATH=modules'
        }
      }
      else if (PROP_BUILD_TYPE == 'cmake') {
        cmake {
          arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"
          cFlags "-D__STDC_FORMAT_MACROS"
          cppFlags "-fexceptions", "-frtti"
        }
      }
    }
  }
 
  buildTypes {...}
 
  productFlavors {
    ...
    demo {
      ...
      externalNativeBuild {
        if (PROP_BUILD_TYPE == 'ndk-build') {
          ndkBuild {
            ...
          }
        }
        else if (PROP_BUILD_TYPE == 'cmake') {
          cmake {
            ...
            targets "native-lib-demo",
                    "my-executible-demo"
          }
        }
      }
    }
 
    paid {
      ...
      externalNativeBuild {
        if (PROP_BUILD_TYPE == 'ndk-build') {
          ndkBuild {
            ...
          }
        }
        else if (PROP_BUILD_TYPE == 'cmake') {
          cmake {
            ...
            targets "native-lib-paid",
                    "my-executible-paid"
          }
        }
      }
    }
  }
 
  externalNativeBuild {
    if (PROP_BUILD_TYPE == 'ndk-build') {
      ndkBuild {...}
    }
    else if (PROP_BUILD_TYPE == 'cmake') {
      cmake {...}
    }
  }
}

buidl.gradleでndk.abiFiltersフラグにABIを設定する。

android {
  ...
  defaultConfig {
    ...
    externalNativeBuild {
      if (PROP_BUILD_TYPE == 'ndk-build') {
        ndkBuild {...}
      }
      else if (PROP_BUILD_TYPE == 'cmake') {
        cmake {...}
      }
    }
 
    ndk {
      abiFilters = []
      abiFilters.addAll(PROP_APP_ABI.split(':').collect{it as String})
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

2021-01-06 [Android]Gradleについての備忘録

_ ビルド要素

ビルド構成ファイル(build.gradle)の要素に対応。

  • ビルドタイプ(debug, release, etc)
    パッケージ化で使用するプロパティを定義。
  • プロダクト・フレーバー(例: 無料版, 有料版)
    異なる種類の定義。
  • ビルド・バリアント
    ビルドタイプとプロダクト・フレーバーを組み合わせたもの。
  • マニフェスト・エントリ
    ビルド・バリアント毎にマニフェスト・ファイルの一部のプロパティ値を設定。
  • 署名
    ビルドの種類毎に署名の内容を設定。
  • コードとリソースの圧縮
    ビルド・バリアント毎にProGuard ルールファイルを指定。
  • 複数 APK サポート
    ビルドの種類毎のコードとリソースを設定。

_ プロジェクト構造

プロジェクト構造の例を図にしてみる。

.
`-- MyApp                           Project(ルート・プロジェクト・ディレクトリ)
    |-- gradle.properties           Gradle プロパティ・ファイル
    |-- local.properties            Gradle プロパティ・ファイル
    |-- build.gradle                トップレベル・ビルド構成ファイル
    |-- settings.gradle             Gradle設定ファイル
    `-- app                         Module
        |-- build.gradle            モジュール・レベル・ビルド構成ファイル
        |-- build
        |-- libs
        `-- src
            |-- main                Sourceset
            |   |-- java
            |   |   `-- com.example.myapp
            |   |-- res
            |   |   |-- drawable
            |   |   |-- layout
            |   |   `-- ...
            |   `-- AndroidManifest.xml
            |-- buildType           特定のビルドタイプのSourceset
            |-- productFlavor       特定のプロダクト・フレーバーのSourceset
            `-- productFlavorBuildType          特定のビルド・バリアントのSourceset

_ Gradle 設定ファイル(settings.gradle)

ビルド対象のモジュールを設定。

include ‘:app’

_ トップレベル・ビルド構成ファイル(build.gradle)

ルート・プロジェクト・ディレクトリにあるbuild.gradle。buildscriptブロックにモジュール共通のGradleリポジトリと依存関係(Android Plugin for Gradle など)を定義。

buildscript {
    repositories {
        google()
        jcenter()
    }
 
    dependencies {
        classpath 'com.android.tools.build:gradle:4.0.0'
    }
}
 
allprojects {
    repositories {
        google()
        jcenter()
    }
}
 
ext {
    compileSdkVersion = 28
    supportLibVersion = "28.0.0"
}

extブロックにモジュール共通のプロパティを定義。このプロパティは、モジュール・レベル・ビルド構成ファイルから利用できる。

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    ...
}
...
dependencies {
    implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
    ...
}

_ モジュール・レベル・ビルド構成ファイル(build.gradle)

モジュール固有の定義。

apply plugin: 'com.android.application'
 
android {
    compileSdkVersion 28
    buildToolsVersion "29.0.2"
 
    defaultConfig {
        applicationId 'com.example.myapp'
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
    }
 
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
 
    flavorDimensions "tier"
    productFlavors {
        free {
            dimension "tier"
            applicationId 'com.example.myapp.free'
        }
 
        paid {
            dimension "tier"
            applicationId 'com.example.myapp.paid'
        }
    }
 
    splits {
        density {
            enable false
            exclude "ldpi", "tvdpi", "xxxhdpi", "400dpi", "560dpi"
        }
    }
}
 
dependencies {
    implementation project(":lib")
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation fileTree(dir: 'libs', include: ['*.jar'])
}

デフォルトのソースセットの設定は変更できる。

android {
    ...
    sourceSets {
        main {
            // デフォルトは'src/main/java'
            java.srcDirs = ['other/java']
 
            res.srcDirs = ['other/res1', 'other/res2']
 
            manifest.srcFile 'other/AndroidManifest.xml'
            ...
        }
 
        androidTest {
            setRoot 'src/tests'
            ...
        }
    }
}
...

_ Gradle プロパティ ファイル

gradle.propertiesにはGradle設定をlocal.propertiesにはローカル環境プロパティ(ndk.dirやsdk.dirなど)を定義する。

モジュールのbuild.gradleで署名が設定できる。

android {
    ...
    defaultConfig { ... }
 
    signingConfigs {
        release {
            storeFile file("my-release-key.jks")
            storePassword "password"
            keyAlias "my-alias"
            keyPassword "password"
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
            ...
        }
    }
}
...

build.gradleに署名情報を記述したくない場合は、ルート・プロジェクト・ディレクトリにkeystore.propertiesを置く。

storePassword=myStorePassword
keyPassword=myKeyPassword
keyAlias=myKeyAlias
storeFile=myStoreFileLocation

モジュールのbuild.gradleのandroidブロックの前にkeystore.propertiesを読み込む定義を記述する。

def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
 
android {
    signingConfigs {
        config {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile file(keystoreProperties['storeFile'])
            storePassword keystoreProperties['storePassword']
        }
    }
    ...
}
...

ビルドするとBuildConfigクラスが生成されるが、カスタムフィールドを追加することができるので、これを使ってアプリのコードに値を渡せる。

android {
    ...
    buildTypes {
        release {
            buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
            resValue("string", "build_time", "${minutesSinceEpoch}")
            ...
        }
        debug {
            buildConfigField("String", "BUILD_TIME", "\"0\"")
            resValue("string", "build_time", "0")
        }
    }
}
...

アプリのコードでは、以下のように参照する。

...
Log.i(TAG, BuildConfig.BUILD_TIME);
Log.i(TAG, getString(R.string.build_time));

マニフェストにも値を渡せる。

android {
    defaultConfig {
        def filesAuthorityValue = applicationId + ".files"
        manifestPlaceholders =
            [filesAuthority: filesAuthorityValue]
        buildConfigField("String",
                         "FILES_AUTHORITY",
                         "\"${filesAuthorityValue}\"")
  }
  ...
}
...

マニフェストでは、以下のように参照する。

<manifest>
    ...
    <application>
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${filesAuthority}"
            android:exported="false"
            android:grantUriPermissions="true">
            ...
        </provider>
    </application>
</manifest>

アプリのコードからの参照は以下の通り。

...
Uri contentUri = FileProvider.getUriForFile(getContext(),
    BuildConfig.FILES_AUTHORITY,
    myFile);

2021-01-12 [Cocoa][Swift]Multiplatform

Xcode 12 の Multiplatform 雛形から生成されるプロジェクトの構成を調べて。

Xcodeで表示される内容は以下の通り。

Xcode

ディレクトリ/ファイル構成は以下の通り。

.
`-- Bedrock
    |-- Bedrock.xcodeproj
    |-- iOS
    |   `-- Info.plist
    |-- macOS
    |   |-- Info.plist
    |   `-- macOS.entitlements
    |-- Shared
    |   |-- Assets.xcassets
    |   |-- BedrockApp.swift
    |   `-- ContentView.swift
    |-- Tests iOS
    |   |-- Info.plist
    |   `-- Tests_iOS.swift
    `-- Tests macOS
        |-- Info.plist
        `-- Tests_macOS.swift

SwiftUIでmacOSとiOSのソースコードは共通化できるので、Sharedディレクトリにソースファイルは置かれ、macOS/iOSディレクトリには、プラットフォーム固有のファイルが置かれている。

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/multiplatform/Bedrock - GitHub

2021-01-19 [Unity][iOS][Android]Unity Native Plug-insについて

Unity Plug-insは外部で作成したコードをUnityに組み込む仕組みで、Managed plug-insとNative plug-insの2種類ある。

大雑把に説明すると、Managed plug-insはC#のコードで、Native plug-insはiOSやAndroidなどのプラットフォーム固有のコードとなる。

Unityの特殊フォルダは以下のとおり。

Assets
アセットが収められるフォルダ。
Editor
ランタイムでなくエディタ用のスクリプトを格納するフォルダ。
Editor default resources
エディタ用リソースのフォルダ。
Gizmos
見えないデザインの詳細を可視化する際に利用するアイコンのフォルダ。
Plugins
プラグインを格納するフォルダ。
Resources
スクリプトで読み込むリソースのフォルダ。
Standard Assets
インポートした標準のアセットパッケージのフォルダ。
StreamingAssets
このフォルダに格納されたファイルはターゲットにコピーされ利用できる。

Native plug-insのネイティブ・ソースを決められた形式のフォルダに格納すると、自動的に統合される。

iOSの場合は、Assets/Plugins/iOS に、ファイル名のsuffix が.a、.m、.mm、.c、.cpp のものが対象となる。

例えば、Assets/Plugins/iOS/Utils.c というファイルを配置する。

float FooPluginFunction()
{
    return 5.0F;
}

このコードを呼び出すC#スクリプトは以下の通り。

using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
 
public class Utils : MonoBehaviour
{
    #if UNITY_IPHONE
    [DllImport ("__Internal")]
    private static extern float FooPluginFunction ();
    #endif
 
    void Awake () {
        #if UNITY_IPHONE
        print (FooPluginFunction ());
        #endif
    }
}

このコードはiOSフレームワークを追加で必要としていないので、iOSビルドすれば動作する。

Androidでは、JavaやKotlinのソースファイルをプラグインに追加できて、InspectorウィンドウのSelect platforms for plubinでAndroidのみ選択されている状態にすればいい。

例えば、Assets/Plugins/Android/Utils.java というファイルを配置する。

package com.example;
 
class Utils {
    public static double fooPluginFunction() {
        return 5.0;
    }
}

このコードを呼び出すC#スクリプトは以下の通り。

using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
 
public class Utils : MonoBehaviour
{
    void Awake () {
        #if UNITY_ANDROID
        using (AndroidJavaClass cls = new AndroidJavaClass("com.example.Utils")) {
            Debug.Log("FooPluginFunction: " + cls.CallStatic("fooPluginFunction"));
        }
        #endif
    }
}

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/unity/AddOn - GitHub

トップ 最新 追記