Citrus-Field TECH BLOG.

フリーランスのITエンジニア、iOSアプリの個人開発、業務委託(小売、ヘルスケア)を行っています。お仕事については、メールもしくはXのDMでご相談ください

原点回帰

そーだいさんの書いたこの記事に刺激をもらった。 モバイルとバックエンドが近いというだけでなく、 なりたい明確な人物像を演じることが必要だ。

理想の自分を演じることで、理想に近づく

メンターあるいはメンタルコーチはいるか。 疑うことを恐れていないか。 コンフォートゾーンで安楽し、hungry精神を忘れていないか。

公立の中高で、場当たり的な受験英語を習って、それが役に立たず人生に活かせなかった悔しさ。

極めて再現性の低い不具合を対処したこと。また、その機会を与えてくださった方々への感謝。

Swiftで今も多用されているクロージャ (Closures)が原因で、循環参照(retain cycle)が生じてメモリリークや不安定な挙動に繋がっていた経験。

フルマラソンで4時間を切れない現実。

倫理観の問題定義、脳死は人の死か、受精卵は人の始まりか、この定義には臓器移植や再生医療などの莫大なビジネスが介在している事実。

自分の行いで結果が大きく変わる。理想の人物に近づく勇気を持っておくこと。

後悔なき人生とコミュニティなど不思議な縁のために。

シンプルなWatchOSアプリを作る

WatchOS用のミニアプリを作る、そのアプリでリューズの回転の使い方も解説します 今回はゼロから新しいアプリを作りたいと思います。

これから作るアプリのスクリーンショット

SwiftUIで実装しています。WatchOSでは、iOSなどと比較し使用できるフレームワークやOSSに成約があるため注意が必要です。

このアプリは、ユーザーが今日食べるべき果物を提案し、もしそれが気に入らない場合は、ボタンをクリックすることで果物を変更するできます。 また、回転するCrownでフルーツの文字サイズを変更できます。

設計

最初に、いくつかの果物を含む配列を定義する必要があります。

@State var fruits = ["🍎 Apple", "🍒 Cherries", "🍓 Strawberry", "🥭 Mango"]

今のところはこれで十分です。(@State アノテーションを追加することを忘れないでください。 これは、この変数が後で変更される可能性があることを意味します)

フルーツを表示してランダムに変化させる

では、今日の果物を表示し、毎ターンランダムに果物が手に入るようにしましょう。

img

var body: some View {
        VStack{
            Text(fruits[0])
                .font(.system(size: 20))
        }
        .padding()
        .onAppear{
            fruits.shuffle()
        }
}

VerticalStackを作成し、それをText要素に入れ、その中にfruits配列の最初の要素を入れています。そして、UIが表示されるたびに配列をシャッフルしています。

ボタンとフッターテキストの追加

同じように、新しい果物をランダムに生成するためのボタンを追加して、デザインを少し美しくしてみましょう。

var body: some View {
        VStack{
            Text(fruits[0])
                .font(.system(size: 20))
            Spacer()
            Button("Random"){
                fruits.shuffle()
            }
            Spacer()
            Divider()
            Spacer()
            Text("Rotate the digital crown to change text size")
                .font(.footnote)
                .multilineTextAlignment(.center)
        }
        .padding()
        .onAppear{
            fruits.shuffle()
        }
    }

私たちのデザインは完成しました。あとは、約束した機能を確実に実現していきます。

機能性

リューズを回転させるトリガーとなるシンプルなシステムを追加する必要があります。これは、私たちが決めた範囲での回転量を指します。

@State var scrollAmount = 10.0

この変数はアプリ内のテキストのサイズを意味します。

Text(fruits[0]).font(.system(size: scrollAmount))

.focusable().digitalCrownRotation($scrollAmount) のコードを VStack に追加します。 (フォーカス可能であることは重要です。これなしではコードは機能しません)

VStack{
    // Our design
}
.focusable()
.digitalCrownRotation($scrollAmount)

これでリューズの回転量の値が分かりました。 しかし、範囲の最小値、最大値、増加量がないという問題がまだあります。 関数に次のようなパラメータを渡します。

VStack{
    // Our design
}
.focusable()
.digitalCrownRotation($scrollAmount, from: 10, through: 20, by:1, isContinuous: false)

最小値、最大値、増加量を決定しました。 WatchOSアプリではテキストが小さすぎたり大きすぎたりするのは適切ではないため、これは重要です。 それらを制限する必要があります。

最終的なコード

すべてのソースコードは下記をご覧ください。

import SwiftUI

struct ContentView: View {
    @State var fruits = ["🍎 Apple", "🍒 Cherries", "🍓 Strawberry", "🥭 Mango"]
    @State var scrollAmount = 14.0
    var body: some View {
        VStack{
            Text(fruits[0])
                .font(.system(size: scrollAmount))
            Spacer()
            Button("Random"){
                fruits.shuffle()
            }
            Spacer()
            Divider()
            Spacer()
            Text("Rotate the digital crown to change text size")
                .font(.footnote)
                .multilineTextAlignment(.center)
        }
        .padding()
        .focusable()
        .digitalCrownRotation($scrollAmount, from: 10, through: 20, by:1, isContinuous: false)
        .onAppear{
            fruits.shuffle()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

動作

現状、日本では、WatchOS用のアプリケーションが諸外国と比較し少ない状況が続いています。 しかし、YAMAPなどWatchOSの制約に負けず高度なUIを実現したアプリは、 WatchOSならではの機能を発揮しています。

yamap.com

一度、WatchOSの可能性を検討してはいかがでしょうか。

テスト駆動開発で確実なロジックを設計、実装するための考え方

AndroidとiOSで、同一機能のネイティブアプリを開発する場合、AndroidとiOSのロジック(アーキテクチャのModel)は一致している必要があります。


アジャイル開発であっても、単体テストは、確実に漏れなく確認します。

正解はドキュメントでは無く、ロジックを確認できるテストコードです。


アーキテクチャがMVVMでもMVPでも、Android とiOSで共通のロジック(Model)が、実装されていなければ、同じ動作を担保できません。 
そのためには、Android側でJUnitを用いたテストコード、iOSでXCTestを用いたテストコード、詳細設計レベルのドキュメント、テスト仕様書をすることが必要です。


現在までにさまざまな開発支援ツールで、 - コードからドキュメントを生成するツール - ドキュメントからコードを生成するツール が、ありましたが、信頼できるものに出会うことは無かったです。それらのツールが生成したコードとドキュメントを人間が問題ないか確認する手間が負担となるためです。

この負担を減らしたくても、ツールの成果物が問題ないか確認する手間は、人間がテストコードを書くよりも信頼できるものはないです。 また、人間であれば、テストコードを書く時点で、仕様の抜け、漏れに気づくことが出来ます。 そして、テストができないコードを書いてしまうミスもなくせます。   ユニットテストは難しいものと考えないでください。   単純な、足し算を行うファンクションのテストコードの、例として、2+3を計算して、結果に5が戻り値になるか、サンプルコードを示します。   Swift、Kotlin、まだAndroidでJavaが使われているプロダクトがあるようなので、Javaのテストコードについても、サンプルコードを示します。  

Swiftで XCTestを使ったユニットテストのコード

 

1. ロジックのコード(Calculator.swift)

class Calculator {
    func add(_ a: Int, _ b: Int) -> Int {
        return a + b
    }
}


2. ユニットテストのコード(CalculatorTests.swift)

import XCTest

@testable import YourAppName
class CalculatorTests: XCTestCase {
    var calculator: Calculator!
    override func setUp() {
        super.setUp()
        calculator = Calculator()
    }

    override func tearDown() {
        calculator = nil
        super.tearDown()
    } 
 
    func testAddition() {
        let result = calculator.add(2, 3)
        XCTAssertEqual(result, 5, "Expected 2 + 3 to equal 5")
    }
}

ポイント

setUp でテスト対象オブジェクトを初期化し、tearDown でクリーンアップします。

XCTAssertEqual を使って、計算結果が期待通りであるかを検証します。

Android(Kotlinの場合)


Androidでは、JUnitとMockitoなどを使用してユニットテストを実行します。こちらも同じように、簡単な加算機能のユニットテストを見てみましょう。  


1. ロジックのコード(Calculator.kt)

class Calculator {
    fun add(a: Int, b: Int): Int {
        return a + b
    }
}


2. ユニットテストのコード(CalculatorTest.kt)


import org.junit.Assert.*
import org.junit.Before
import org.junit.Test

class CalculatorTest {
    private lateinit var calculator: Calculator

    @Before
    fun setUp() {
        calculator = Calculator()
    }

    @Test
    fun testAddition() {
        val result = calculator.add(2, 3)
        assertEquals(5, result)
    }
}

ポイント:

@Before アノテーションを使って、テスト前に必要なセットアップを行います。

assertEquals を使って、期待する結果と実際の結果を比較します。

Android(javaの場合)

1. ロジックのコード(Calculator.java)

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
}


2. ユニットテストのコード(CalculatorTest.java)

import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;

public class CalculatorTest {
    private Calculator calculator;
    @Before
    public void setUp() throws Exception {
        calculator = new Calculator();
    }

    @Test
    public void testAddition() {
        int result = calculator.add(2, 3);
        assertEquals(5, result);
    }
}

  ポイント:

@Before アノテーションで、各テストメソッドの実行前に setUp メソッドが呼ばれ、Calculator インスタンスを初期化します。

assertEquals を使用して、計算結果が期待通りであることを検証します。


Javaを使ったAndroidのユニットテストでは、JUnitを使い、Kotlinの時と同様にロジックの検証が可能です。

every one Happy Cording!

Figmaでデザイン、ワイヤーフローを作成しプロトタイプモードで画面遷移図を確認する

私は、Figmaでデザイン、プロトタイピング、VSCode連携、FigJamなど、なるべくFigmaで完結できるような環境を使っています。 ただし、開発モード(Dev Mode)は、時期尚早と判断し、まだ使っていませんが、今後の進化で有効となれば使い始めると思います。

ここでは、Figmaで画面遷移図を作成する手順をまとめておきます。

Figmaのデザイン、プロトタイピングについて

1. 画面レイアウトをデザインする

まず、各画面のデザインをFigma上で作成します。画面遷移図では、画面がどう遷移するかを視覚化するために、各UI画面が必要です。

  • Figmaを開き、新しいプロジェクトやファイルを作成します。
  • 必要なUIのデザインをFigmaのフレームツールを使って作成します。フレームは「F」キーを押して簡単に作成できます。

2. プロトタイプモードに切り替える

画面遷移を設定するために、プロトタイプモードを使います。

  • Figmaの右上の「プロトタイプ」タブをクリックします。
  • 画面(フレーム)間の遷移を設定できるようになります。

3. リンクを設定する

各画面間の遷移を作るために、リンク(フロー)を設定します。

  • プロトタイプモードにいる状態で、遷移元の画面の要素を選択します。例えば、ボタンやリンク部分を選びます。
  • その要素から矢印が表示されるので、それを次に遷移させたい画面にドラッグします。
  • 設定ウィンドウが表示されるので、遷移のトリガー(クリックやタップなど)やアニメーション(スライド、フェードなど)を選びます。

4. 遷移アニメーションを設定する

遷移の動き(トランジション効果)を設定します。

  • 「インタラクションディテール」パネルで、遷移時のアニメーションをカスタマイズできます。
    • Instant(瞬時)、Dissolve(フェードイン・アウト)、Move In(スライドイン)などを選べます。

5. フローを完成させる

全ての画面間で必要な遷移を設定します。必要に応じて、分岐やループする部分も考慮して、自然な流れになるようにフローを作り上げます。

6. 画面遷移図をプレビューする

作成した遷移図はFigma上でプレビューできます。

  • Figma右上の「プレイボタン」(再生ボタン)を押すと、プロトタイプの動作を確認できます。 タップで画面遷移する。

タップで画面遷移する。

タップで画面遷移する。

タップで画面遷移する。

7. 共有してフィードバックを受ける

Figmaのプロトタイプはリンクを共有することで、他のメンバーと簡単に共有し、フィードバックを得られます。

  • プロトタイプビューにある共有ボタンを使ってリンクを生成し、他の人に送りましょう。

追加のヒント

  • コンポーネントを使う: 画面内の繰り返し使われるUI要素はコンポーネント化しておくと効率的です。
  • 矢印を使う: デザインだけでなく、明確に画面遷移を示すために矢印や説明ラベルを追加することで、より視覚的に分かりやすい遷移図を作れます。

Figmaの「Dev Mode」について

Figmaの「Dev Mode」を使うと、デザイナーと開発者が効率的にコラボレーションできるように、デザインから直接コードスニペットを生成できます。 FigmaのDev Modeを使ってiOS向けのコードスニペットを生成する手順を紹介します。

1. Figma Dev Modeを有効化する

まず、FigmaのDev Modeに切り替えます。

  • プロジェクト内のデザインファイルを開いた状態で、右上の「Dev Mode」タブに切り替えます(ツールバー上の「<>」アイコンをクリック)。
  • Dev Modeに切り替えると、デザイン要素の詳細やコードスニペットが表示されるインスペクタが使えるようになります。

2. iOS向けコードスニペットの表示

デザイン要素からiOS向けのコードスニペットを生成するには、特定のデザイン要素を選択します。

  • デザインファイル上で、iOSのUIに関連するボタン、テキスト、アイコン、コンテナなどの要素をクリックして選択します。
  • 右側のプロパティパネルに、選択した要素のプロパティやコードスニペットが表示されます。

3. コードスニペットの形式を選択する

FigmaのDev Modeでは、複数のプラットフォームに対応したコードスニペットを生成できます。iOSの場合、SwiftObjective-Cのコードが生成可能です。

  • 要素を選択した状態で、右側の「コード」タブにアクセスします。
  • プラットフォームのドロップダウンメニューから「iOS」を選びます。これにより、iOS向けのコードスニペット(通常はSwift)が表示されます。

4. コードスニペットの内容

選択したデザイン要素に応じて、Figmaは以下のようなコードスニペットを自動生成します。

  • カラー: UIColorまたはSwiftUI.Colorのコード(例: UIColor(red: 0.95, green: 0.76, blue: 0.22, alpha: 1.0))。
  • テキスト: UILabelSwiftUI.Textのコード(例: Text("Button"))。
  • フォント: UIFontSwiftUI.Fontの設定コード。
  • サイズやレイアウト: ビューのサイズや制約に関するコードが表示されます(例: frame(width: 100, height: 44))。

5. SwiftUIとUIKitの切り替え

Figmaは、SwiftUI向けのコードとUIKit向けのコードを両方生成できます。

  • SwiftUIのコードが必要な場合は、プラットフォームのオプションでSwiftUIに切り替えると、より適切なコードスニペットが表示されます。
    • 例: Text("Hello, World!")
  • UIKitのコードが必要な場合は、iOSの通常設定で、UILabelUIButtonなどのUIKitコードが表示されます。

6. コードスニペットのコピー

生成されたコードスニペットをコピーするには、スニペットが表示されている部分の右端にあるコピーアイコンをクリックします。これで簡単にスニペットをコピーして、Xcodeなどの開発環境に貼り付けます。

7. 追加のカスタマイズ

Figmaから生成されるコードは基本的なスタイルやプロパティに基づいていますが、アプリの具体的な要件に合わせて手動でカスタマイズが必要です。例えば、SwiftUIであれば、Figmaが生成するframepaddingの値を調整したり、UIKitであればAuto Layoutの制約をさらに設定することが求められます。

まとめ

Figmaの開発モード(Dev Mode)を使うことで、デザイナーと開発者間のコミュニケーションがスムーズになり、デザインからiOS向けのSwiftやUIKitのコードスニペットを簡単に生成できます。 Figmaから提供されるコードをベースに、アプリ開発のスピードアップに繋がります。 開発モード(Dev Mode)は、有料ですが、今のところ、価格に見合う機能ではないと考えています。

具体的なiOSコードの例は割愛します。

iOS開発環境の構築を半自動化するヒント

環境構築めんどくさいですよね。 だからシェルを流してバババってやりたいと思っています。

osascriptとはAppleScriptを実行するためのコマンドです

まずはカレントディレクトリでのシェル

#!/bin/sh

echo "\n**********************"
echo "Xcodeのセットアップ"
echo "************************\n"
xcode-select --install


echo "\n**********************"
echo "Homebrewのインストール"
echo "************************\n"
which -s brew
if [[ $? != 0 ]] ; then
    # Install Homebrew
    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
else
    brew update
fi


echo "\n**********************"
echo "Carthageのインストール"
echo "************************\n"
which -s carthage
if [[ $? != 0 ]] ; then
    brew install carthage
else
    echo "Carthage Found, Skipping Install. \n"
    echo "Updating Carthage if Necessary. \n"
    brew upgrade carthage
fi


echo "\n**********************"
echo "Checking for SwiftFormat and SwiftFormat Plugin for Xcode"
echo "(you can access from xcode→editor→swift format menu item)"
echo "************************\n"
which -s swiftformat
if [[ $? != 0 ]] ; then
    brew install swiftformat
    brew install --cask swiftformat-for-xcode
else
    brew upgrade swiftformat
    brew upgrade --cask swiftformat-for-xcode
fi


echo "\n**********************"
echo "Xcode用SwiftFormatとSwiftFormatプラグインのチェック"
echo "(xcodeの[editor]-[swift format menu item]で確認できます"
echo "************************\n"
which -s swiftlint
if [[ $? != 0 ]] ; then
    brew install swiftlint
    brew install --cask swiftformat-for-xcode
else
    brew upgrade swiftlint
    brew upgrade --cask swiftformat-for-xcode
fi

# SwiftLintForXcodeのファイルが残っている場合は、ダウンロードを続行する前に削除
rm "SwiftLintForXcode.zip"

# SwiftFormat xcode プラグインをダウンロード
wget https://github.com/norio-nomura/SwiftLintForXcode/releases/download/0.1/SwiftLintForXcode-0.1.zip -O SwiftLintForXcode.zip


# 前のバージョンがインストールされている場合は上書き
unzip -o SwiftLintForXcode.zip -d /Applications/

sleep 1;

# SwiftLintForXcodeのファイルを削除
rm "SwiftLintForXcode.zip"

open -a Finder /Applications/SwiftLintForXcode.app;

sleep 3;

# xcodeで使用する前に、これらのアプリを一度開く必要がある
open ".swiftformat" -a "SwiftFormat for Xcode";
osascript -e 'tell application "Terminal" to activate'; sleep 3;  osascript -e 'tell application "SwiftFormat for Xcode" to quit';

open -a "SwiftLintForXcode";
osascript -e 'tell application "Terminal" to activate'; sleep 3;  osascript -e 'tell application "SwiftLintForXcode" to quit';


# プラグインを有効にする
osascript -e 'tell application "System Preferences" to activate'
osascript -e 'tell application "System Preferences" to set current pane to pane "com.apple.preferences.extensions"'
osascript -e 'display alert "Please Check `SwiftFormat` to Enable  XcodePlugin!"'

echo "\n**********************"
echo "初期設定完了"
echo "************************\n"

そのあと、プロジェクフォルダに移動して以下を実行します

cd [プロジェクフォルダ]

#!/usr/bin/env sh

if ! command -v carthage > /dev/null; then
  printf 'Carthage is not installed.\n'
  printf 'See https://github.com/Carthage/Carthage for install instructions.\n'
  exit 1
fi

set -euo pipefail

xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX)
trap 'rm -f "$xcconfig"' INT TERM HUP EXIT

CURRENT_XCODE_VERSION=$(xcodebuild -version | grep "Build version" | cut -d' ' -f3)
echo "EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1500__BUILD_$CURRENT_XCODE_VERSION = arm64 arm64e armv7 armv7s armv6 armv8" >> $xcconfig

echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1500 = $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1500__BUILD_$(XCODE_PRODUCT_BUILD_VERSION))' >> $xcconfig
echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' >> $xcconfig

export XCODE_XCCONFIG_FILE="$xcconfig"
carthage "$@"
# Workaround for Carthage build error END

carthage bootstrap --platform iOS --cache-builds --no-use-binaries

抜け、漏れがあるかもしれません Carthageや、その他、プロジェクトに応じて、修正してみてください。

Play Console 要件対応、D-U-N-S ナンバー登録を行う。

Androidアプリのリリースに、D-U-N-S ナンバー(DUNS ナンバー)が必要になりました。

詳しくは、以下に記載されています。

support.google.com

 

これまでも、Apple Bussiness Manager対応などで企業様のD-U-N-Sナンバーを聞くことはありましたが、私のような個人事業主のアプリ屋がD-U-N-Sナンバーを登録する時代になったということは、悪質アプリ対策の一環だと思います。

D-U-N-S® Numberとは、世界5億件超の企業を一意に識別できる9桁の企業識別コードです。D&Bが独自に管理をしており、国内企業についてはTSRが管理しています。

東京商工リサーチ様より引用

 

D-U-N-Sナンバー取得については、こちらに素晴らしい記事があります。

blog.katsubemakito.net

 

上記の時期では、FAXが必要となっていますが、今も同様か確認したいと思います。また、決済は銀行振り込みらしいので、法人口座も整理しようと思っています。

 

不確定な部分もあるので全て完了したら、この記事を更新したいと思います。