トップ 追記

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|

2020-06-21 [Cocoa][Swift]toggle()

今更ですが、便利なメソッドが用意されているのですね。

var isPlaying: Bool = false
isPlaying.toggle()
// isPlaying == true

2020-05-24 [Cocoa][Swift]COVIDs19 Exposure Notifications API

COVIDs19 Exposure Notifications APIは、Bluetoothを使った追跡機能を実装するために、AppleとGoogleが提供するフレームワークだ。このフレームワークを組み込んだアプリケーションを利用すれば、感染者と接触したことを知らせてくれるようになる。

Appleはサンプルコードを公開していて、これを参考にすれば、サーバの応答がシミュレートされているので、サーバ側の実装も参考になるもののようだ。

サンプルコードの実行に必要な条件が、この機能の利用条件か分からないが、この機能を利用するためにAPIが追加されることを考えると、多分、そうだろう。

  • iOS 13.5以上が動作する端末
  • Xcode 11.5以上

サンプルコードの内容な自分の予想以上に色々やっていて、これは個人情報保護のための工夫が理由のようだ。


2020-04-26 [Cocoa][Swift]Swift Package Manager

CocoaPodsやCarthageのようなパッケージ管理ツールの利用は、少々敬遠していたのだが、Xcode 11からSwift Package Managerと呼ばれるパッケージ管理ツールが利用できるようになったということで調べてみた。

swift.orgのPackage Managerを説明したページで、PlayingCardというサンプル・クラスのパッケージ構造が説明されているが、これをSwift Package Managerのツールを使って生成してみる。

以下のコマンドで、PlayingCardパッケージを生成する。


$ mkdir PlayingCard
$ cd PlayingCard
$ swift package init --type=executable
Creating executable package: PlayingCard
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/PlayingCard/main.swift
Creating Tests/
Creating Tests/LinuxMain.swift
Creating Tests/PlayingCardTests/
Creating Tests/PlayingCardTests/PlayingCardTests.swift
Creating Tests/PlayingCardTests/XCTestManifests.swift

gitでバージョン管理をされることを想定した.gitignoreや単体テスト関連の雛形ファイルが生成されている。

ソースファイルはSources/PlayingCard配下に置くことになる。main.swiftという雛形ファイルを生成されているので、これを削除して、swift.orgのPackage Managerで紹介されているPlayingCardのサンプルのPlayingCard.swiftとRank.swift、Suit.swiftを配置することになる。


PlayingCard
├── Package.swift
├── README.md
├── .gitignore
├── Sources
│   └── PlayingCard
│       ├── PlayingCard.swift
│       ├── Rank.swift
│       └── Suit.swift
└── Tests
    ├── LinuxMain.swift
    └── PlayingCardTests
        ├── PlayingCard.swift
        ├── Rank.swift
        ├── Suit.swift
        └── XCTestManifests.swift

上記では、Tests/PlayingCardTests配下もサンプルの内容に変更している。

Swiftのバージョンの違いが理由だと思うが、生成されたPacjage.swiftの内容は、swift.orgのサンプルと異なっている。


// swift-tools-version:5.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
 
import PackageDescription
 
let package = Package(
    name: "PlayingCard",
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages which this package depends on.
        .target(
            name: "PlayingCard",
            dependencies: []),
        .testTarget(
            name: "PlayingCardTests",
            dependencies: ["PlayingCard"]),
    ]
)

Package.swiftでは他パッケージとの依存関係が記述できる。

これをgithubなどで公開すれば、Xcodeの File > Swift Packages > Add Package Dependency... で利用できるようになる。


2020-03-01 [Cocoa][Swift][Xcode]使うXcodeのバージョンを切り替える

自社のアプリケーション開発では、常に最新のOSとXcodeを使用しているので考えることはなかったが、 大規模なプロジェクトに参画して仕事をする場合、複数のXcodeをインストールして、切り替えながら使わないといけないことがある。

そんな際に役立つのが、xcode-selectコマンドだ。

_ 複数バージョンのXcodeをインストールする

多くの人に役立つよう、基本的なことから説明する。

常に最新バージョンのXcodeを望むなら、App Store経由でインストールする。

希望したバージョンのXcodeを望むなら、Developerサイト(https://developer.apple.com)の Account → Downloads → More の "More Downloads for Apple Developers" からインストールする。その場合、Xcode.app の名前を Xcode_11.3.1.app と変えて、/Applications に置くことをお勧めする。名前に space があると後で困ることになるので避けること。

_ Command Line Toolsをインストールする

Xcodeをインストールしたら、先ずは起動する。すると、Command Line Toolsのインストールが始まる。

以下のコマンドでもインストールを開始できる。

xcode-select --install

著者は "More Downloads for Apple Developers" からも、Command Line Tools for Xcode をインストールしている。理由は、単体でインストールするとclangのModulesでなくなった、/usr/include にヘッダーファイルがインストールからだったが、現在ではインストールされなくなったようだ?

_ Xcodeのバージョンを切り替える

複数あるXcodeと関連するBSDツールを切り替えるのが xcode-select コマンドだ。

xcode-select --switch  path/to/Xcode.app

ただし、実際にはスーパーユーザー特権で実行する必要があるので、以下のように実行する。

$ sudo xcode-select --switch /Applications/Xcode.app

切り替えが上手くいったかは、以下のコマンドで確認できる。

$ xcode-select --print-path

以下のコマンドでも確認できる。

$ /usr/bin/xcrun --find xcodebuild

2020-02-24 [Android][Kotlin]Androidの設定値について

指定したターゲット(パッケージ名や利用するサービスの設定)でAndroidアプリケーションをビルドする方法は複数ある。初期のものであったり、それを改善したものであったり。最新の方法だと利点が多いので選択できればいいのだが、歴史が長いソースコードだと、様々な理由から方法を変更できなかったり。

何がベストなのかは状況によって異なるが、まずは知らないことには話にならないということで調べてた方法を列挙してみる。

_ androidコマンド

最新のAndroid SDKのツールではなくなったコマンド。オプションで指定したAndroidアプリケーションの雛形となるソース一式を生成されるので、例えば、異なるパッケージ名のソースを生成する場合に使っていた。

$ android create project \
    --name プロジェクト名 \
    --target ターゲットID \
    --path プロジェクトを生成するファイル・パス \
    --package パッケージ名 \
    --activity デフォルト・アクティビティ名

_ 代替リソース

条件毎に用意したディレクトリにリソースを置いて切り替える方法。

以下は高密度画面(hdpi)とそれ以外の画面の端末でアイコン画像を切り替える例だ。

res/drawable/icon.png
res/drawable-hdpi/icon.png

hdpiは設定修飾子と呼ばれる識別子だが、どんな設定修飾子があるのか表にしてみる。

設定修飾子の値説明
MCC と MNCmcc310 mcc310-mnc004モバイル カントリー コード(MCC)とモバイル ネットワーク コード(MNC)
言語と地域en en-rUS b+en+USISO 639-1言語コード BCP 47言語タグ
レイアウトの方向ldrtl ldltrldrtlは右から左。ldltrは左から右。
smallestWidthswdp使用可能な画面領域の最小寸法で指定。
使用可能な幅wdp使用可能な最小の画面幅をdp単位で指定。
使用可能な高さhdp使用可能な最小の画面の高さをdp単位で指定。
画面サイズsmall normal large xlarge
画面アスペクトlong notlong
円形の画面round notround
広色域widecg nowidecg
HDRhighdr lowdrハイ ダイナミック レンジ
画面の向きport land
UI モードcar desk ...端末をホルダーに装着したり、取り外す場合
ナイトモードnight notnight
画面ピクセル密度ldpi xxxhdpi ...画面ピクセル密度(dpi)
タッチスクリーン タイプnotouch finger
テキスト入力方法nokeys qwerty 12key
ナビゲーション キーnavexposed navhidden
ナビゲーション方法nonav dpad trackball wheel
プラットフォーム Ver.v3 v4 v7 ...API レベル

_ ローカライズ

設定修飾子を使った代替リソースを利用する方法。例を挙げる。

res/values/strings.xml
res/drawable/
res/values-en/strings.xml
res/values-ja/strings.xml
res/drawable-ja/

文字列リソースを英語と日本語で切り替えるのと、画像を日本語とそれ以外で切り替える例となっている。

_ ビルド バリアント

ビルド・バリアントは、ビルドタイプとプロダクト フレーバーを組み合わせたも。build.gradleの例だ。

android {
    defaultConfig {
    }
 
    signingConfigs {
        release {}
    }
 
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
        debug {}
        staging {}
    }
 
    flavorDimensions "api", "mode"
    productFlavors {
        demo {
            dimension "mode"
        }
        full {
            dimension "mode"
        }
 
        minApi24 {
            dimension "api"
        }
        minApi23 {
            dimension "api"
        }
        minApi21s {
            dimension "api"
        }
    }
 
    variantFilter { variant ->
        def names = variant.flavors*.name
        if (names.contains("minApi21") && names.contains("demo")) {
            setIgnore(true)
        }
    }
}

ビルド バリアントの形式は、<product-flavor><Build-Type>となる。

例えば、minApi24DemoDebugは、minApi24とdemoとdebugの組み合わせという意味。この場合、apkはapp-minApi24-demo-debug.apkとなる。

ビルド・バリアントに対応したJavaソースのパスは、例えば src/demoMinApi24/java/ となる。

ソースセットの優先順位の例だ。

  • src/demoDebug/
  • src/debug/
  • src/demo/
  • src/main/

_ gradle.properties

build.gradleに渡すパラメータを設定できる。

gradle.propertiesの例。

DEMO_STRING="demo string"
DEMO_NUM_DEMO=1234
DEMO_NUM_FULL=5678
DEMO_BOOLEAN=true

build.gradleの例。

android {
    defaultConfig {
        // resの値を追加(AndroidManifest.xml で参照)
        resValue 'string', 'DEMO_STRING', "${DEMO_STRING}"
 
        // BuildConfigクラスのメンバーを追加(Kotlinコードで参照)
        buildConfigField 'boolean', 'DEMO_BOOLEAN', "${DEMO_BOOLEAN}"
    }
    productFlavors {
        demo {
            // BuildConfigクラスのメンバーを追加(Kotlinコードで参照)
            buildConfigField 'long', 'DEMO_NUM', "${DEMO_NUM_DEMO}"
        }
        full {
            // BuildConfigクラスのメンバーを追加(Kotlinコードで参照)
            buildConfigField 'long', 'DEMO_NUM', "${DEMO_NUM_FULL}"
        }
}

リソースの値を設定することでAndroidManifest.xmlで使い値を切り替えたり、BuildConfigクラスのメンバーを設定することで、ソースコードで参照する値を切り替えられる。


2020-01-13 [macOS][Kotlin]null安全

Cocoa + Objective-C では、nilは許容されるものでnilに対してメソッド呼び出しを行なってもアボートしないが、Javaだとnullアクセスは例外が投げられてしまう。そのような考えの違いが、null安全についても、SwiftとKotlinで差となっているのかな?

JavaではC言語の基本的データ型に相当するのが基本型(プリミティブ型)で、C言語の構造体に相当するクラスはC言語のポインタ型に相当する参照型(リファレンス型)となる。そして、C言語と同様に値渡しのみとなる。

Kotlinでは全ての型はオブジェクトで、それは参照型(リファレンス型)となる。参照型だとJavaではnullを代入できるが、Kotlinではnullが代入できるnull許容(nullable)と、null非許容(non-nullable)がある。

null許容は型名に?を付ける。

var a: String? = null

null非許容は型名に?を付けない。以下はコンパイルでエラーとなる。

var a: String = null

null許容の変数は、nullの可能性があるので、そのまま利用するとコンパイルでエラーとなる。

fun main(args: Array) {
    var s: String? = "demo"
    var n = s.length
    print(n)
}
Error:(3, 14) Kotlin: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?

セーフコール演算子(?.)を使うと、nullでない場合は評価され、nullだと回避される。

var s: String? = "demo"
var n = s?.length
print(n)

セーフコール演算子を使う場合は、nullでないことの確認を行うことになると思うが、素直に書くと以下のようになる。

var s: String? = "demo"
if (s != null) {
    var n = s?.length
    print(n)
}

もっとスマートに書くために、セーフコール演算子と一緒に使えるlet関数が用意されている。

var s: String? = null
s?.let {
    var n = it.length
    print(n)
}

デバッグなので強制的にnullでないものとして使うための非null表明演算子(non-null assertion operator)、または、二重感嘆符演算子(double-bang operator)と呼ばれる演算子(!!.)がある。

var s: String? = "demo"
var n = s!!.length
print(n)

別のnullチェックのやり方として、null合体演算子(null coalescing operator)、または、エルヴィス演算子と呼ばれる演算子(?:)がある。

var s: String? = null
var t = s ?: ""
var n = t!!.length
print(n)

let関数とnull合体演算子を組み合わせると、以下のように書ける。

var s: String? = null
s?.let {
    var n = it.length
    print(n)
} ?: print("s is null.")

Kotlinで特徴的なのは、文法的にnullチェックされているのが分かっている場合は、コンパイラをnullチェック済みとするところだ。

var s: String? = "demo"
if (s == null) {
    print("s is null")
    return
}
var n = s.length
print(n)

これは開発環境を提供している会社が作ったプログラミング言語だからなせる技か。Kotlinには似たような割り切りを感じる部分が他にもある。機会があれば後で取りあげたい。


2020-01-02 [macOS][Kotlin]結果とエラー情報を持つデータ

Javaでは結果を関数の戻る値で、エラー情報は例外で、だったが、Androidは例外を勧めていないことは以前から感じていた。Kotlinでは、例外は復旧できない致命的な状況で利用とし、既存のJavaモジュールが投げてくる例外を包み込みResult型があったりしている。

自分でKotlinでプログラミングする際、あえて例外を利用する必要はないと思うので、どんなやり方が合うのか調べてみて辿り着いたコードを紹介する。参考にしたのは、ここ

AndroidはActivityやFragmentが再生成されるということから、ActivityやFragmentのメンバー変数でModelを持たないだとか、再生成されてもModelが破棄されない。ModelはActivityやFragmentをメンバー変数で持っていると再生されると破棄されたものにアクセスしてしまう問題がある。それの解決策としてJetpackのViewModelやLiveDataがあるのだが、LiveDataではエラーを例外で渡せない。結果とエラー情報がLiveDataとなっているのが扱いやすいということがあるので、結果とエラー情報を持つ型を用意することにした。

sealed class FindUserResult {
    data class Found(val user: User) : FindUserResult()
    data class NotFound(val name: String) : FindUserResult()
}

Swiftだとenumを利用すればだが、Kotlinのenum classは状態を定数で持つだけ。その代わり、sealed classを使えば値を持てる。そして、サブクラスを用意することによって、状態を持てる。

上の例では、FindUserResultのサブクラスがFoundとNotFoundになっていて 、プライマリコンストラクタでそれぞれのプロパティのuserとnameを宣言している。

この型を戻り値にした関数がこれ。

fun findUserByName(name: String): FindUserResult {
    ....
    if (見つかった) return FindUserResult.Found(user)
    else          return FindUserResult.NotFound(name)
}

成功したらFoundを返している。失敗の場合はNotFoundを返している。処理の結果や、エラー情報はコンストラクタのパラメータで設定している。

この戻り値を受け取った側のコードがこれ。

val result = findUserByName("bitz")
when (result) {
    is FindUserResult.Found -> println("find ${result.user}")
    is FindUserResult.NotFound ->println("find ${result.name}")
}

型で成功か失敗が分かり、プロパティで結果やエラー情報が取れる。


2020-01-01 [macOS][Kotlin]開発環境を用意する

仕事でAndroidアプリケーションをKotlinで開発しているので、macOSでKotlinを試してみる。

KotlinといえばAndroidStudioでAndroidアプリケーションをプログラミングだと思うが、macOS上で動作するKotlinで書かれたプログラムを動かしたいので、開発環境としてIntelliJ IDEAを使うことにする。IntelliJ IDEAはUltimate EditionとCommunity Editionがあるか、Kotlinプログラミングの学ぶ目的なら、Community Editionで大丈夫だ。JetBrains社のWebサイト (https://www.jetbrains.com/ja-jp/idea/download/#section=mac) からダウンロードしよう。

download

IntelliJ IDEAが入手できたら、Java仮想マシンで動作するプログラムのプロジェクトを生成する。

新規プロジェクト
Kotlin_JVM
Project name

この状態では、Kotlinのソースファイルは作られていないので、srcフォルダを右クリックして、New > Kotlin File/Class を選択して、Kotlinファイルを生成する。

new file
Kotlin File

そして、hello, worldを印字するコードを書く。

fun main(args: Array) {
    println("hello, world")
}

Run.

run

hello, world と印字されている。


2019-12-24 [Cocoa][Swift]Cocoa.swiftのご案内

2019年も、もうすぐ終わります。今年一年ありがとうございました。
勉強会って参加するのもいいですが、運営するのも色々得るものがあるというのも再認識できた一年でした。

以下が、今年開催された勉強会の一覧です。

  • Cocoa.swift 2019-01 (macOS/iOSアプリケーション開発勉強会)
    • 第116回 Cocoa勉強会 関東 / MOSAオフラインミーティング
    • 2019年1月16日
    • 池袋コワーキングスペース OpenOffice FOREST
    • https://cocoa-kanto.connpass.com/event/111294/
  • Cocoa.swift 2019-02 (macOS/iOSアプリケーション開発勉強会)
    • 第117回 Cocoa勉強会 関東 / MOSAオフラインミーティング
    • 2019年2月20日
    • 池袋コワーキングスペース OpenOffice FOREST
    • https://cocoa-kanto.connpass.com/event/117577/
  • Cocoa.swift 2019-03 (macOS/iOSアプリケーション開発勉強会)
    • 第118回 Cocoa勉強会 関東 / MOSAオフラインミーティング
    • 2019年3月13日
    • 池袋コワーキングスペース OpenOffice FOREST
    • https://cocoa-kanto.connpass.com/event/121658/
  • Cocoa.swift 2019-04 (macOS/iOSアプリケーション開発勉強会)
    • 第119回 Cocoa勉強会 関東 / MOSAオフラインミーティング
    • 2019年4月24日
    • 池袋コワーキングスペース OpenOffice FOREST
    • https://cocoa-kanto.connpass.com/event/124467/
  • Cocoa.swift 2019-06 (macOS/iOSアプリケーション開発勉強会)
    • 第120回 Cocoa勉強会 関東 / MOSAオフラインミーティング
    • 2019年6月12日
    • 池袋コワーキングスペース OpenOffice FOREST
    • https://cocoa-kanto.connpass.com/event/129470/
  • Cocoa.swift 2019-07 (macOS/iOSアプリケーション開発勉強会)
    • 第121回 Cocoa勉強会 関東
    • 2019年7月17日
    • 池袋コワーキングスペース OpenOffice FOREST
    • https://cocoa-kanto.connpass.com/event/135361/
  • Cocoa.swift 2019-09 (macOS/iOSアプリケーション開発勉強会)
    • 第122回 Cocoa勉強会 関東
    • 2019年9月11日
    • 池袋コワーキングスペース OpenOffice FOREST
    • https://cocoa-kanto.connpass.com/event/145138/
  • Cocoa.swift 2019-10 (macOS/iOSアプリケーション開発勉強会)
    • 第123回 Cocoa勉強会 関東
    • 2019年10月30日
    • 池袋コワーキングスペース OpenOffice FOREST
    • https://cocoa-kanto.connpass.com/event/147201/

昨年度と比較して開催時期がほぼ同じと安定していとうか進歩がないというか。


2019-11-30 [Cocoa][Swift]XCFramework

Darwinで採用されています実行形式のバイナリ・フォーマットMach-oは、一つのファイルに複数のアーキテクチャのバイナリが格納できるという素晴らしい特徴があるのですが、同じCPUで異なるシステム向けのバイナリは同時に格納できないという欠点があるようです。以前だと、これで問題はなかったのですが、例えば、iPad OS向けアプリのソースからmacOSアプリを作ることができるUIKit for Mac (Catalyst)だと、x86_64でiOSとiPhoneシミュレータ(macOS)という場合が発生して、同一ファイルに格納できないという問題が発生します。

おそらく、これの対策として用意されたのが、Xcode 11から利用できるXCFramework。簡単に説明すると複数のフレームワークを一つにできるというものだ。

具体的には、MoltenGLというライブラリはiOSとmacOS向けのフレームワークが用意されていて、これを以下のコマンドでXCFrameworkにまとめられる。

% xcodebuild -create-xcframework \
> -framework MoltenGL-0.25.0/MoltenGL/iOS/framework/MoltenGL.framework \
> -framework MoltenGL-0.25.0/MoltenGL/macOS/framework/MoltenGL.framework \
> -output MoltenGL.xcframework
xcframework successfully written out to: MoltenGL.xcframework

この中身をtreeコマンドで確認してみる。

% tree MoltenGL.xcframework
MoltenGL.xcframework
├── Info.plist
├── ios-armv7_arm64
│   └── MoltenGL.framework
│       ├── Headers
│       │   ├── MoltenGL.h
│       │   ├── mglDataTypes.h
│       │   ├── mglEnv.h
│       │   ├── mglGLKitDataTypes.h
│       │   ├── mglMetalState.h
│       │   ├── mglext.h
│       │   └── mln_env.h
│       └── MoltenGL
└── macos-x86_64
    └── MoltenGL.framework
        ├── Headers -> Versions/Current/Headers
        ├── MoltenGL -> Versions/Current/MoltenGL
        └── Versions
            ├── A
            │   ├── Headers
            │   │   ├── MoltenGL.h
            │   │   ├── mglDataTypes.h
            │   │   ├── mglEnv.h
            │   │   ├── mglGLKitDataTypes.h
            │   │   ├── mglMetalState.h
            │   │   ├── mglext.h
            │   │   └── mln_env.h
            │   └── MoltenGL
            └── Current -> A
 
10 directories, 18 files

単なるx86_64でなく、macOSのx86_64となっている。


トップ 追記