2023年10月15日日曜日

iOS よくわからないエラー集「Undefined symbol:...」

ライブラリをアプリに組み込んでビルドしたら、以下のエラーが出力されたが、知ってしまえばなんてことはない。

Undefined symbol: _GULLogBasic
Undefined symbol: _OBJC_CLASS_$_***
Undefined symbol: _OBJC_CLASS_$_***
Undefined symbol: _OBJC_CLASS_$_***
Undefined symbol: _OBJC_CLASS_$_***

「それ実機でしかビルドできないのに、シミュレータを対象にビルドしているよ」というときに表示されるエラーなので、「シミュレータでも動作するライブラリを使う」か、それが無理なら「シミュレータをターゲットにビルドするときは、該当のライブラリを組み込まずに使う、という方針となる。

2023年10月14日土曜日

アジャイル開発 と スクラム開発 スクラムイベントついて解説

どうも。どっことです。今回は、アジャイル開発(アジャイル)とスクラム開発の違い、そしてスクラム開発におけるイベントについてします。

アジャイル

アジャイルとは「俊敏性の高いソフトウェア開発」のことを指します。

スクラム(スクラム開発)

そして、スクラムとは「俊敏性の高いソフトウェア開発」の手法のひとつです。つまりスクラム開発はアジャイルに分類される手法の一つとなります。

固定的かつ長期的なメンバーで開発が進行することを想定しています。それによりチームワークを高まり、結果として生産性の最大化やビジネス目標の達成を図ります。

スクラム開発ではプロダクトオーナー、開発者、スクラムマスターの役割があり、各役職はメンバーに割り当てられます。

スクラムイベントについて

スクラム開発には4つのプロセス(イベント)があります。

1.スプリントプランニング

スプリントの最初に実施するプロセスです。

プロダクトバックログ(プロジェクトで消化すべきタスクの一覧)から優先度の高いアイテムを抽出し、タスク化します。タスク化する対象は、メンバーで認識相違がないことをあらかじめ確認したうえで決まります。そうしてタスク化されたアイテムがそのスプリントで消化するバックログ(スプリントバックログ)となります。

2.デイリースクラム

平日の決まった時間(午前中、特に朝が一般的)に短い時間でのミーティングを実施し、進捗確認や要求変化の確認を行います。スクラムマスターが全員に「今日は何やりますか」と質問し、その回答を確認することだけが実施される。問題があれば、スクラムマスターは別途会議などを設定することで解決を促しますが、スクラムマスターは、その問題解決意思決定の責務を負います。

3.スプリントレビュー

スプリント後に実施します。開発されたソフトウェアのレビューを行い、必要に応じてバックログをアップデートします。製品の機能や品質が十分であると判断されるまでスプリントとスプリントレビューを繰り返されます。

4.スプリントレトロスペクティブ

いわゆる振り返りです。スプリントの最後に実施します。

「スプリントの成果」にフォーカスが宛てられるスプリントレビューに対して、スプリントレトロスペクティブは「スクラムチームの動き」などの、プロセスにフォーカスされる。例えば、以下の事項について議論されます。

  • 直近のスプリントについての評価
  • 良かった点や今後の課題
  • スプリントの中で課題を解決した事例
  • 次回以降のスプリントにおける改善点

補足

アジャイル開発にはスクラム以外にもエクストリーム・プログラミング(XP)やカンバンなどがあげられます。

まとめ

今回は、アジャイル開発(アジャイル)とスクラム開発の違い、そしてスクラム開発におけるイベントについて解説しました。

参考

2023年10月13日金曜日

HTML/CSS CSSを設定するときに頻出する設定項目

このブログの表示感を調整するためにCSSをカスタマイズしていたが、毎回調べなおしていたので備忘録。

  • width
    • タグ要素の幅。divで頻出。
  • height
    • タグ要素の幅。divで頻出。
  • background-color
    • タグ要素内の背景色。RGB指定。
  • font-size
    • タグ要素内のテキストのテキストサイズ。 指定方法が色々ある。
    • px,emとか。
    • larger/smaller (親要素のフォントサイズとの相対)とか。
  • color
    • タグ要素内のテキストの色。RGB指定。
  • font-style
    • タグ要素内のテキストのスタイル。
    • normalitalicobliqueとか。
  • padding
    • タグ要素内に設定する余白。px,em指定。
    • 1つ指定は全方向に同一の値を設定。
    • 4つ指定はの順に設定される。
  • margin
    • タグ要素外に設定する余白。px,em指定。
    • 1つ指定は全方向に同一の値を設定。
    • 4つ指定はの順に設定される。
  • list-style
    • リストタグ(ul)限定。左につくボッチの指定。
    • noneを指定すると消える。
  • border-style
    • 枠線を表示する際のスタイルを指定できる。
    • solid(実線),dashed(破線)など。
  • border-width
    • 枠線を表示する際の枠線の太さ設定。
    • px, em指定。
  • border-color
    • 枠線を表示する際の枠線の色指定。
    • RGB指定。
  • border
    • border-style, border-width, border-colorを一括で指定できるプロパティ。

2023年10月12日木曜日

iOS ボタンタップ中の色を変えたい

ViewのプロパティであるisHighlightedの更新を受けて、背景色を設定するだけ。

カスタムViewを実装する際に、isHighlightedをオーバーライドし、didSetbackgroundColorを設定すればいいだけ。こんなん。

override open var isHighlighted: Bool {
    didSet {
        backgroundColor = isHighlighted ? .lightGray : .blue
    }
}

ちなみにisHighlightedとは、Androidでいうところのstate_pressedUIButtonなどのUIControlクラスを継承したViewは状態に対して背景を変えるメソッドがあるため、それを使えばタップ中の色を変えることができる。

参考

2023年10月11日水曜日

iOS UIViewを角丸にしたり、枠線を付ける実装方法を解説

どうも。どっことです。今回は、iOSのUIViewについて角丸や枠線をつけ方について実装を解説していきたいと思います。

iOSで角丸や枠線を付ける実装方法

私自身がAndroidエンジニアとしてやってきた時間が長いので、どうしてもAndroid開発者目線となってしまうのですが、Viewを角丸にしたり、枠線をつけたりする実装が本当に面倒くさいです。簡単に見せるだけでも、新規に背景用ファイルを用意してあげる必要があります。

しかし、iOSは角丸や枠線を超簡単に実装できます。本当に羨ましいです。別に悔しくねーし。

実装方法

それでは、実装方法について解説していきたいと思います。

角丸にする

UIViewが共通に持っているlayerという変数に対応するパラメータがあるので、コード上であれば以下に値を設定することで角丸にすることができます。

layer.cornerRadius

枠線を付ける

枠線の実装も同様で、以下に値を設定することで枠線を付けることができます。

layer.borderWidth
layer.borderUIColor

ちなみに、borderUIColorCGColorクラスを設定する必要があることに注意してください。

まとめ

今回は、UIViewを角丸にしたり枠線をつけたりする実装方法を解説しました。特に見せ方を柔らかくする角丸の実装が簡単なのは、iOSはとても良いなと思いました。

参考

2023年10月6日金曜日

Android ファイルアクセスが拒否されるせいでビルドが全然通らない問題と解決方法を紹介

どうも。どっことです。今回ですが、ここ2,3年、AndroidStudioでのビルドが20回に1回程度しか成功しなくなり開発モチベが著しく低下していたが、ついにその問題を解決することができたので備忘録として記載します。

現象と根本の原因

How to solve this error app:packageDebug FAILED

A failure occurred while executing com.android.build.gradle.tasks.PackageAndroidArtifact$IncrementalSplitterRunnable java.io.FileNotFoundException: C:\Users\HP\AndroidStudioProjects\Chatter\app\build\intermediates\incremental\packageDebug\tmp\debug\zip-cache\androidResources (Access is denied)

このエラーが解決できずに、この2,3年本当に困っていました。「はー。もうダルイし、Macでも買うか。」とここぞとばかりに物欲全開に検討していました。

そして解決へ

そんなこんなでPCのリプレースも検討していたのですが、上記のページをみていくとコメント欄に以下の記述がありました。

The actual problem is that your anti-virus is preventing the access to the files. Make the Android Studio application as trusted application(recommended) or try turning off your antivirus.(おまえのPCのウイルス対策ソフト、ちゃんと設定しとるんか?AndroidStudioを信頼できるアプリに設定しとるか?あとは試しにソフトの機能をOFFにしてビルドしてみ?)

私「...」
私「...」
私「それだわー」

ウイルス対策ソフトの機能をOFFにしてビルドしたところ、何事もなかったかのようにビルドが成功しました。

ウイルス対策ソフトをインストールしてからすぐのAndroid Studio起動で「なんか知らんツールがアクセスしたがってるけどええか?」という警告が表示されていたので脳死でOKを設定していましたが、それだけじゃ足りなかったようです。公式サイト(参考に記載)にも「ビルドのパフォーマンスが著しく低下するで?ちゃんと設定するんやで?」というアナウンスがされていました。

原因が分かったので、ウイルス対策ソフトの設定に対象外フォルダを設定することで無事に解決しました。本当に良かった。

最後に

私のPCではソースネクスト社のウイルスセキュリティソフトをインストールしています。絶対にツッコませない。このソフトでの対象外フォルダ指定手順を紹介して終了したいと思います。

  1. ウイルスセキュリティソフト のホーム画面を開く
  2. 設定 > ウイルス・スパイウェア対策 >設定(セクションタイトル下部に導線有)
  3. ウイルス自動検知タブ > 検査したくないファイル、フォルダを指定する
  4. ファイルやフォルダを追加で検査対象外のフォルダを追加する。以下にチェックを入れる。
    • ウイルス自動検知の対象にしない
    • サブフォルダも含む

参考

2023年10月4日水曜日

Android gradleのキャッシュを削除するコマンドを紹介

どうも。どっことです。今回は、毎回忘れるGradleのキャッシュ削除のコマンドを備忘録として紹介します。

gradleのキャッシュを削除するコマンド

早速ですが、コマンドを載せます。

rm -rf ~/.gradle/cashes

~/となっていることを見ても分かる通り、削除するディレクトリはアプリプロジェクトにあるディレクトリではなくユーザディレクトリにあるキャッシュのディレクトリになります。これを実行すると、次回ビルドは非常に時間がかかるのが見込まれますので、「PCやプロジェクトがおかしいな?」と思った時にやるのがちょうどいいと思います。

ちなみに .gradle フォルダを削除しても、Gradle がビルドする時に再構築してくれるらしいので気にすることは全くありません。

独り言

ただ、最近AndroidStudioのビルドが全く通らない。

C:\Users\XXXXX\Documents\project\sampleProject\app\build\intermediates\incremental\packageDebug\tmp\debug\zip-cache\androidResources (アクセスが拒否されました。)

いったいなんだってばよ。

追記。なんか解決した。新規に記事にした。

2023年9月21日木曜日

Android つぶやきながらJetpackComposeのサンプルを覗いてみる その1

巷ではJetpackComposeが流行っているらしい。これまでAndroid開発では xml により画面のレイアウトを実装していたけど、今後は 宣言型UIフレームワーク が主流になってくる様子。xml が慣れている人にとってはまだすこし取っ掛かりにくいけど、画面の構成がより明示的に表現されるのであれば、それはそれで嬉しいと思う。

サンプルを見てみる。

Android Studio>File>New>Import Sample...で、入力フォームにcomposeを入力すると、それっぽいものがフィルタリングされるので、適当に選択する。Androidarchitecture>Architectureを選択してみる。(どうやらTODOアプリのサンプルが見れる様子。SunFlowerも画像表示のサンプルとしては重要だけど、今回はシンプルそうな方を眺めてみる)

とりあえずDLが終わったけどどこを見ればいいのかわからないので、AndroidManifest.xmlを見てみる。TodoActivity というActivityが登録してあるみたい。

TodoActivityを覗いてみる

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContent {
    AppCompatTheme {
      TodoNavGraph()
    }
  }
}

setContentしか実装されていない... AppCompatThemeは名前からなんとなくテーマが設定されているコンポーネントかと予想できるので、その内側に設定されているTodoNavGraph を見てみる。(deprecatedなっていたが、趣旨から外れるのでここでは気にしないことにする。)

TodoNavGraphを覗いてみる

で、初っ端にでてきた実装がこれ。

@Composable
fun TodoNavGraph(
  modifier: Modifier = Modifier,
  navController: NavHostController = rememberNavController(),
  coroutineScope: CoroutineScope = rememberCoroutineScope(),
  drawerState: DrawerState = rememberDrawerState(initialValue = DrawerValue.Closed),
  startDestination: String = TodoDestinations.TASKS_ROUTE,
  navActions: TodoNavigationActions = remember(navController) {
    TodoNavigationActions(navController)
  }
) {
...

fun... メソッドやんけ...しかも戻り値もない...ほんとうにこれでいいんか...?もう少し眺めてみよう。

@Composableアノテーションが付与されたメソッド内の実装を眺めてみる。

どうやらNavHostというコンポーネント内で、必要なものを構成するぽい。

NavHost(
  navController = navController,
  startDestination = startDestination,
  modifier = modifier
) {

んで、その NavHost での実装は composable というコンポーネントで構成されている。

composable(
  TodoDestinations.TASKS_ROUTE,
  arguments = listOf( navArgument(USER_MESSAGE_ARG) { type = NavType.IntType; defaultValue = 0 }
  )
) { ... }
composable(TodoDestinations.STATISTICS_ROUTE) { ... }
composable(
  TodoDestinations.ADD_EDIT_TASK_ROUTE,
  arguments = listOf(
      navArgument(TITLE_ARG) { type = NavType.IntType },
      navArgument(TASK_ID_ARG) { type = NavType.StringType; nullable = true },
  )
) { ... }

で、そのコンポーネントが各画面やサイドメニュー(ドロワー)の要素に対応していそう。

まとめ(所感)

ここまでの所感など

  • TodoDestinations で定義された定数が、このアプリを構成する画面やドロワーに対応していそうに見える。であれば実際に運用する場合この定義は列挙型にしておいて、引数としてはordinalなどを渡してあげたほうが良さそうな。
  • TodoDestinations.ADD_EDIT_TASK_ROUTEcomposable を見ると、どうやら画面に渡すパラメータもここで定義しておく様子。ここでの定義と実際の画面とで乖離が起きないよう、画面側のどこかで管理しておいたほうが良さそう。
  • アプリを構成する画面定義は見えたけど、各画面間での画面遷移や処理結果の戻しなどをどのように実装するかは見えていないので、次にサンプルを眺めるときはこの辺りをフォーカスして確認できれば嬉しい。

とりあえずここまで。

参考

2023年9月17日日曜日

Android ViewのコンストラクタとカスタムView実装に伴うスタイル定義について解説

どうも。どっことです。今回は独自ViewとしてEditTextのカスタマイズクラスを実装したときにハマった問題について、備忘録を載せたいと思います。

Viewのコンストラクタとスタイル定義

ListViewなどを使うために追加する独自View(①)と、TextViewなどのコンポーネントのViewをカスタマイズするView(②)とで、特にコンストラクタの実装方針が異なります。今回はそんなコンストラクタの実装方針とそれに伴うスタイル定義について解説していきます。

コンストラクタについて

そもそもAndroidのView派生クラスには、3つのコンストラクタがあります。

1.Contextのみを引数とするコンストラクタ

constructor(context:Context)

これはコードからViewを生成するときに使われるコンストラクタです。LayoutInflaterを使わない場合は、このコンストラクタでViewを生成するコースがほとんどかと思います。属性値がほぼ初期値なので、ViewGroupaddView()するときに自分で値を設定する必要があります。

2.Context, AttributeSetを引数とするコンストラクタ

constructor(context:Context, attrs:AttributeSet?)

これはxmlからViewを追加したときに呼ばれるコンストラクタです。

3.Context, AttributeSet, defStyleAttrを引数とするコンストラクタ

constructor(context:Context, attrs:AttributeSet?, defStyleAttr: Int)

これもxmlからViewを追加したときに呼ばれるコンストラクタです。「2.と何が違うの?」という疑問はAndroidアプリ開発者なら誰しも一度は思う謎ですが、それはxml内にstyleが指定されているか否かの違いです。xml内でstyleが指定されていれば3.、されていなければ2.が呼び出されます。

コンストラクタの実装方針について

それではコンストラクタを紹介したところで、先ほどの①と②での実装方針の違いについて説明していきます。

①の場合

気にしなくて問題ありません。以下のようなコードをコピペして使います。(以下はLinearLayoutの派生クラスとして定義した時の例です。)

class HogeView(context: Context, attrs: AttributeSet?, defStyleAttrs:Int) : LinearLayout(context, attrs, defStyleAttrs) {
  constructor(context: Context): this(context, null, 0)
  constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0)
  // ...
}

②の場合

こちらは適切なスタイルの定義が必要になります。デフォルトの場合、例えばEditTextはこのような実装になっています。

public EditText(Context context) { // ①のコンストラクタ
    this(context, null);
}
public EditText(Context context, AttributeSet attrs) { // ②のコンストラクタ
    this(context, attrs, com.android.internal.R.attr.editTextStyle);
}
public EditText(Context context, AttributeSet attrs, int defStyleAttr) { // ③のコンストラクタ
    this(context, attrs, defStyleAttr, 0);
}

第4引数については参考を見てもらうとして2.のコンストラクタが3.のコンストラクタをオーバーロードしているのは同じですが、引数が異なっています。①の場合は0 を渡していましたが、こちらはcom.android.internal.R.attr.editTextStyleを渡しています。

EditTextStyleにはEditTextが必要としている各属性の初期値が設定されており、これを渡してやらないと非常に残念な表示になってしまいます。(EditTextなのに、テキストが編集できないとか。1敗。)特に気にしないなら、これと同じように com.android.internal.R.attr.editTextStyleを渡してやれば問題ありませんが、「せっかく独自Viewを追加したのだから、スタイルもいい感じにしたい」ということであれば、追加で以下を実装する必要があります。(今回はEditTextを例として挙げます。)

実装手順

属性の追加

とりあえず何も考えずに attrs.xml に以下を追加します。

名前も適当です。必要に応じて適切な名前にしてください。

<resources>
    <attr name="hogeEditTextStyle" format="reference" />
</resources>

スタイルの追加

続いて何も考えずに styles.xml に以下を追加する。

名前も...(略)

<resources>
    <style name="HogeEditTextStyle" parent="android:Widget.EditText">
    </style>
</resources>

テーマの追加

前項までに追加したものをつなぐために、themes.xml に以下を追加します。

な...(略)


<style name="Theme.AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
    <!-- ... -->
    <!-- Customize your theme here. -->
    <item name="hogeEditTextStyle">@style/HogeEditTextStyle</item>
</style>

コンストラクタでの参照

最後に、「属性の追加」で追加したものを参照します。

class HogeEditText(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
    AppCompatEditText(context, attrs, defStyleAttr) { // ③のコンストラクタ
    constructor(context: Context) : this(context, null, R.attr.hogeEditTextStyle) // ①のコンストラクタ
constructor(context: Context, attrs: AttributeSet?) : this( // ②のコンストラクタ context, attrs, R.attr.hogeEditTextStyle ) //... }

必要な実装は以上です。これでカスタムしたHogeEditTextでも残念な表示にならず、いい感じの表示とすることができます。

余談

第3引数が必要なコンストラクタの変数名が defStyleAttrとなっており、これは default Style Attributeを意味しているものと考えられます。つまり、デフォルトのStyleのattributeが設定されることを想定されているということです。xml上でstyleが設定されていなければ②の、styleが設定されていれば③のコンストラクタが呼ばれる点からもこの想定が正しいものと思われます。独自Viewとして想定しているデフォルトのスタイルなどは、①、②の場合に限らず、無理やりコード上で設定するのではなく今回紹介した方法のdefStyleAttr設定するのがスマートな実装方法のように感じられます。

まとめ

今回はコンストラクタと、カスタムView実装におけるスタイル定義について解説しました。カスタムViewの実装は、ソースコードの整理やロジックをまとめる際に非常に有効な手法です。今回のように、必要なコンストラクタがどれか、スタイル定義が必要か、などを理解した上で効率よく開発していきたいですね。

参考

2023年9月1日金曜日

Python HTTP通信で情報取得

どうも。どっことです。通信取得する処理について紹介します。今回はPythonでサーバ通信・情報取得する処理です。

説明

さっそくサンプルを載せます。pythonをインストールできていれば、以下でOKです。

import sys
import json 
import urllib.request

# 取得したい情報のURL
url = "取得したい情報のURL"
header = {
  # 例えば header に仕込む key-value
  'key': 'value', 
  # 例えば header に仕込む 認証情報
  'Authorization': 'Bearer <YOUR TOKEN>'
}
req = urllib.request.Request(url, headers = header)

with urllib.request.urlopen(req) as res:
    body = json.load(res)
    print(body)

Android/iOSといったネイティブアプリのように、非同期処理などのことを考えなくていいので、とても簡単ですね(逆に不安になる)。巷ではrequestsといった、通信処理を簡単に実装できるようなライブラリはごまんとあるようですが、今回は「可能な限り簡単に使えるスクリプト」を意識したいので、標準のurllibを使いました。

参考

移行予定

どうも。どっことです。 タイトルの通りですが、諸事情により GitHubPage に移行予定です。 https://mkt120.github.io/ この備忘録に記載の内容を転記しつつ、今後はこちらのページを更新していく予定です。