くま's Tech系Blog

基本的には技術で学んだことを書き留めようと思います。雑談もやるかもね!

Linuxコマンド覚書

Linuxコマンドってかなり多いですよね。 しかもたまになんだっけと思いながらググるので、その時一つのサイトでわかればいいなあと思ったので、まとめたいと思います。 (主に自分用ですが)

おそらく随時更新するでしょう。たぶん.....

cpコマンド

ファイルやディレクトリのコピー

// fileAをdirBのディレクトリにコピー
 cp fileA dirB

// fileAのディレクトリの内容をすべてdirBとしてコピー
cp -r fileA dirB
catコマンド
// fileA とdirBを連結して内容を表示
cat fileA  dirB
findコマンド
//  /etc以下で.txtのファイルを検索
find /etc-name "*.txt"

 find . -type d -user root
  →現在のディレクトリ上のオーナーがrootのディレクトリをすべて検索します。
touchコマンド
//  fileAが存在していれば、最終アクセス日時を現在の時刻に更新、存在していなければ、空ファイルとしてfileAを作成
touch fileA
lsコマンド

ファイルやディレクトリの情報を表示

//  オプション
-a      ...すべてのファイルを表示します。
-l      ...ファイルの詳細情報を表示します
                  (ファイル名、ファイルタイプ、パーミッション、ハードリンクの数、オーナー名、グループ名、ファイルサイズ、タイムスタンプ)。
-R      ...サブディレクトリも再帰的に表示します。
-F      ...ファイルタイプを表す記号をつけて表示します(ディレクトリ: /  実行可能ファイル : *)
pwdコマンド

現在作業している場所を表示

pwd
grepコマンド

ファイルやディレクトリの情報を表示

//  オプション
-G      ...検索に正規表現が使用できます。
-F      ...固定文字列の検索ができます。
-i      ...検索条件として、大文字小文字の区別をなくします。
 -正規表現-
.       改行文字以外の任意の1文字。
*       直前の1文字の0回以上の繰り返しに一致。直前の文字は正規表現でもOK。
^       行の先頭を表します。
$       行の末尾を表します。
 []      括弧内の任意の文字に一致。ハイフン(-)で範囲を指定することもできます。括弧内の最初の文字を^すると意味が逆転します。
+       直前の文字の1個以上の連続。
?       直前の文字の0または1文字に一致。
a1|a2       a1またはa2のいずれかに一致。
 \       正規表現に使われる記号を普通の文字として扱います。
chmodコマンド

アクセス権を変更

//  オプション
u     オーナー
 g     グループ
 o     その他のユーザー
 a     すべてのアクセス権
// 変更方法
 +     指定したアクセス権を付加します。
 -     指定したアクセス権を削除します。
 =     指定したアクセス権にします。
 r     読み込み権限
 w     書き込み権限
 x     実行権限

 // 数字表記
 0     ---
 1     --x
 2     -w-
 3     -wx
 4     r--
 5     r-x
 6     rw-
 7     rwx

Flutterのmain.dartを見てみよう

今回はFlutterのmain.dartを見ていこうと思います

main.dartを見ることで一連の流れやどこに何を書いていけばいいのか理解出来ると思ったので、まとめてみようと思いました

早速、見てみましょう!!

main.dartは下記です

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also a layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Invoke "debug painting" (press "p" in the console, choose the
          // "Toggle Debug Paint" action from the Flutter Inspector in Android
          // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
          // to see the wireframe for each widget.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

大まかに3〜4箇所に分けられるので、それぞれ説明できればと思います

main

void main() => runApp(MyApp());

main()はアプリケーションのエントリーポイント(最初に続行されるプログラム)となる関数です

Javaを触ったことある人なら馴染みがあるかもしれません

runApp()は実装されているWidgetを画面に描画します(Widgetは後で補足します)

MyApp()はWidgetを返却するクラス。これはアプリケーションコードとして自動生成されるので、Widget型を返却すれば名前は何でも大丈夫です

Widget

次はWidgetについてです

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}


class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

その前にWidgetとは何かというところですが、簡単に言うとアプリの「見た目に関わる部分」を構成する部品(パーツ)です

そのWidgetのなかにStatelessWidgetStatefulWidgetの2種類がありますがこの違いはなんでしょうか?

StatelessWidgetで扱う値はすべて不変的でありプロパティを変更することはできず、すべての値はfinalな値となります

StatefulWidgetではWidgetの生存期間中に変更される値を維持することができます

なので一例ですが、StatelessWidgetではボタンの固定の文言を定義して、StatefulWidgetでは場合によって変わる文言を定義したりします

固定文言等の状態を持たないものはStatelessWidgetが継承されている箇所に実装すれば良いです

一方の状態を保持するものはStateクラスのインスタンスを作成するStatefulWidgetクラスStateクラスの2種類が必要になります

今回のmain.dartでは、StatefulWidgetStatefulWidgetを継承したMyHomePageと、Stateを継承した_MyHomePageStateの2種類から構成されています

2種類で構成されているのは、StatefulWidgetは不変であるWidgetを継承していて、StatefulWidget自体は不変である必要があるからです

しかし、StatefulWidgetは可変である必要があるため、Stateに可変の要素を持たせています

これを元にStatefulWidgetにコメントを加えたいと思います

class MyHomePage extends StatefulWidget {

  // コンストラクター。クラス名({this.変数名})とすることで自動的に渡された親Widgetから渡された値を変数に設定する
  MyHomePage({Key key, this.title}) : super(key: key);

  // 親Widgetから不変の値を受け取る場合、finalを宣言する
  final String title;

  // ステートクラスをインスタンス化する。
  @override
  _MyHomePageState createState() => _MyHomePageState();
}


class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  // 変数の値を変更する際、setStateを必ず呼ぶ
  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

ここから、StatefulWidgetの特徴は以下の5つがあることがわかります

①親Widgetから不変の値を受け取る場合、finalを宣言する

コンストラクターで変数に値を設定

➂Stateクラスをインスタンス化する

➃Stateクラスで変数の値を更新する場合は、setStateを呼ぶ

➄親Widgetで受け取った変数にアクセスする場合、widget.変数名を用いる

また、State<MyHomePage>とすることでMyHomePageの変数にアクセス出来るようになっています

build

最後に、build関数は描画するWidgetを返します

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also a layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Invoke "debug painting" (press "p" in the console, choose the
          // "Toggle Debug Paint" action from the Flutter Inspector in Android
          // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
          // to see the wireframe for each widget.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

コメントを見るとわかるようにsetStateが呼ばれるたびにbuild関数が実行されます(StatefulWidgetの場合)

全体の更新ではなく、更新する部分だけの更新でパフォーマンスの向上を図っているようです

StatelessWidgetでも描画するWidgetを返す場合はbuild関数を使います

Scaffoldは色々なWidgetを受け取り、それをマテリアルデザインに準拠したレイアウトで配置します

親子関係

最後に何回か親Widgetという表現が出てきていますが、これは一体どういうことなんでしょうか?

それは先ほどのbuildのコードを見るとわかります

  @override
  Widget build(BuildContext context) {
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also a layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Invoke "debug painting" (press "p" in the console, choose the
          // "Toggle Debug Paint" action from the Flutter Inspector in Android
          // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
          // to see the wireframe for each widget.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),

ソースをざっと見るとbodyのchildがColumnになっていると思います

なので、bodyが親でColumnが子の関係にあります

また、そこからさらにchildren: とあるようにTextが子の関係にあります

このようにWidgetは親子関係になり普通に実装すると、ネストが深くなります

まとめ

main.dartを見るだけでもFlutterのルールなどがわかります

また、Reactなどが似たような思想を持っている部分もあるので、勉強してみるのもいいかもしれないです

何より「全てはWidget」と言われるほど、Widgetの考えは根幹になると思うので、理解するのが第一だと感じました

参照

A tour of the Dart language

Flutter Doc JP

Gitのサブモジュールについて

Gitのsubmoduleについて詳しくなかったので、記事にしました

サブモジュールとは

Gitのサブモジュール機能を使うと、プロジェクトで管理しているGitリポジトリとは別に、独立したリポジトリをプロジェクトに含ませることができます

サブモジュールを含んだプロジェクトでは、push・pullをしてもサブモジュール側のリポジトリには影響がありません

サブモジュールにするものは他リポジトリでも使うような共通機能であることが多いです

また、サブモジュールはあくまで特定コミットを示すポイントを追加したディレクトリに埋め込むものです

実際にどうやるの?

定番の操作について簡単に説明します

前提として、サブモジュール用のリポジトリとサブモジュール使うリポジトリの2種類が存在しています

サブモジュール用のリポジトリは下記画像です

f:id:kumaskun:20200719230654p:plain
サブモジュール用リポジトリ

そして、サブモジュール使うリポジトリは下記です

f:id:kumaskun:20200719231348p:plain
サブモジュールを使うリポジトリ

まずはサブモジュールを追加します

サブモジュール使うリポジトリにサブモジュール用のリポジトリを取り込むイメージです

git submodule add https://github.com/yasu1234/submodule_sample.git // サブモジュールを追加
git commit -a  // 変更を保存

このとき必ず変更を保存してください!!

こうすることで、サブモジュール使うリポジトリにはサブモジュール用のリポジトリであるsubmodule_sampleのフォルダが作成されます

f:id:kumaskun:20200719231923p:plain

コミット後にプッシュすると以下のようになります

f:id:kumaskun:20200719232411p:plain

.gitmodulesというファイルと、サブモジュールのディレクトリが追加されています

別の人がtest_submoduleの最新版を落としてきた時に.gitmodules内の情報を元にサブモジュールのリモートリポジトリに接続しにいきます

また、サブモジュールの部分を見るとわかるようにコミットIDで管理されています。

仮にサブモジュールを更新して、その修正を反映したい場合はサブモジュールのコミットIDの変更をコミット・プッシュする必要があります

クローンする場合

サブモジュールを含むプロジェクトをクローンする場合は単純にそのプロジェクトをクローンすればいいわけではありません

実際は複数のプロジェクトが入り混じっているのですから

そういうときは。recursiveオプションをつけることで解決します

git clone --recursive https://github.com/yasu1234/test_submodule.git

recursiveオプションをつけることでサブモジュールを初期化、アップデートしています

なので、もしクローン時にrecursiveオプションをつけ忘れた場合は下記コマンドでクローン後に実施出来ます

git submodule update --init --recursive

サブモジュールを削除する場合

削除する場合は下記コマンドを実行します

git submodule deinit 【サブモジュールのパス】

rm -rf .git/modules/【サブモジュールのパス】

上記のコマンドが.gitmodules.git/configの設定を自動で消してくれるため、git configの編集は必要ないです

最後に、サブモジュールはいろんなプロジェクトで使う共通ファイルや処理をまとめるのに便利な機能ですが、とりあえず共通化しているからつめこもうという気持ちでサブモジュールに追加すると後々面倒なことになるので、少し考えて追加するかを検討しましょう!!

参照

Gitのサブモジュール機能を使ってプロジェクトを管理してみよう

Git submoduleの押さえておきたい理解ポイントのまとめ

iOSのQRコード読み取りについて

今回はiOSQRコードを読み取る処理について書こうかと思います

使用バージョンは以下の通りです

Swift 5.0

カメラ起動

QRコードを読み取る処理で最初にやることと言えば、カメラ起動です

まずは、カメラ起動の流れを見ていきましょう

カメラに起動にはAVFoundationというフレームワークを使います

カメラなどのデバイスを表すAVCaptureDeviceクラスで取得した入力データを、AVCaptureDeviceInputクラス(サブクラス)を用いてAVCaptureSessionに渡します

AVCaptureSessionクラスから出力されるデータは、出力形式を管理するAVCaptureOutputクラスによって、画像、動画、フレームデータ、音声データ、メターデータなど様々な形式で出力されます

つまりはAVCaptureSessionAVCaptureInputAVCaptureOutputを追加し、キャプチャのアクティビティ全体を管理するものです

まずは、デバイスの設定をしてセッションに追加します

カメラと言っても、背面カメラや前面カメラ、ビデオ撮影やフォト撮影など色々あるので、どのタイプのカメラを使うのかを設定します

class ViewController: UIViewController {
    // デバイスからの入力と出力を管理するオブジェクト
    var captureSession = AVCaptureSession()
    // キャプチャーの出力データを受け付けるオブジェクト
    var output = AVCaptureMetadataOutput?
   // カメラプレビュー表示用のレイヤ
   var cameraPreviewLayer: AVCaptureVideoPreviewLayer?

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // デバイス一覧を取得(ビデオを指定)
        let videoDevices = AVCaptureDevice.device(for: AVMediaType.video)
        
        //  カメラを前面で起動する場合の処理
        for device in videoDevices {
            let device = device
            if device.position ==  AVCaptureDevice.Position.front {
                let videoDevice =  device
           }
       }

  // 前面カメラからvideoInputを取得
  let videoInput : AVCaptureDeviceInput? =  try! AVCaptureDeviceInput(device: videoDevice!)

     // セッションに追加
       if videoInput != nil {
           captureSession.addInput(videoInput!)
           output = AVCaptureOutput()
           captureSession.addOutput(output!)
           output.addMetadataObjectsDelegate(self, queue: DispatchQueue.main)   
           output!.metadataObjectTypes = output!.availableMetadataObjectTypes
       }
    } 
}

次に、カメラの取得している映像の表示をします

ここでLayerが出てくるのですが、LayerはViewに描画する内容を管理するオブジェクトです

カメラの取得している映像を画面に表示するには、AVCaptureVideoPreviewLayerクラスを使います

class ViewController: UIViewController {
    // デバイスからの入力と出力を管理するオブジェクト
    var captureSession = AVCaptureSession()
    // キャプチャーの出力データを受け付けるオブジェクト
    var output = AVCaptureMetadataOutput?

    override func viewDidLoad() {
        super.viewDidLoad()
        
        //  デバイスの設定をしてセッションに追加した後の処理はここ

  self.cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
       // キャプチャの縦横比を維持した状態で表示するように設定
       self.cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
       // プレビューレイヤの表示の向きを設定
       self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
       self.cameraPreviewLayer?.frame = view.frame
  
  // カメラの向きを設定
  switch(UIApplication.shared.statusBarOrientation) {
       case UIInterfaceOrientation.portrait:
           self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientaion.portrait
       case UIInterfaceOrientation.portraitUpsideDown:
           self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientaion.portraitUpsideDown
       case UIInterfaceOrientation.landscapeLeft:
           self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientaion.landscapeLeft
       case UIInterfaceOrientation.landscapeRight:
           self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientaion.landscapeRight
       case .unknown:
           self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientaion.portrait
       @unknown default:
           self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientaion.portrait
        }

       self.view.layer.insertSublayer(self.cameraPreviewLayer!, at: 0)

  // セッションの開始
  captureSession.startRunning()
}

ここまででカメラの起動ができました

次にQRコードの読み取りを行う処理を実装します

QRコード読み取り

QRコードの読み取りはAVCaptureMetadataOutputObjectsDelegateで行います

extension ViewController: AVCaptureMetadataOutputObjectsDelegate {
    
    func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection){
        for object in metadataObjects {
            let data = object
            if !(data isKind(of: AVMetadataMachineReadableCodeObject.self)) {
                continue
            }

            // QRのデータの場合読み取りを行う
            if(data.type == AVMetadataObject.ObjectType.qr) {
                 // ここにスキャン後の処理を入れる

         captureSession.stopRunning()
                 break
            }
        }
    }
}

ここまででQRの読み取りが行えます

一連の流れを理解するのに時間がかかるかもしれないですが、カメラはiOS開発でよく使うと思うので、理解する必要があると思いました。

最後にカメラのアクセス許可をInfo.plistで設定する必要があります

keyにPrivacy - Camera Usage Descriptionを設定して、valueには理由などを記載します

こうすれば、カメラが起動するときにアクセス許可が求められるようになり、アプリが落ちることはなくなります

参照

Swiftでカメラアプリを作成する(1)

【メモ書き】Swift4でQRコードリーダーを作ろうとした時…

AndroidでRealmのインポートが上手くいかない

今回は小ネタです

新しいライブラリを追加してsyncしていると下記エラーが発生しました

A problem occurred evaluating project ':app'.
> Failed to apply plugin [id 'realm-android']
   > Configuration with name 'kapt' not found.

realm-androidでエラーが発生しているっぽい・・・

今まで動いていたのになぜと思いながら調べてみるとpluginの順番が関係しているようでした

apply plugin: 'realm-android'
apply plugin: 'kotlin-kapt'

build.gradle(app)の中でpluginを適用している箇所があるのですが、上記のようになっていました

順番が間違っているようで、入れ替えると治りました

apply plugin: 'kotlin-kapt'
apply plugin: 'realm-android'

今まで動いていたのはなぜだという疑問がありますが、動いてよかったです

ご存知の方がいればぜひ教えてください!!

参照

Stackoverflow

Delegateとは一体??

Swiftでよく使うDelegateってよくわかんないなあ

と、最初は思っていましたが、実装していくうちにわかってきました

概念がわかりにくいかもしれませんが、必ずと言っていいほど1度は通る道なので軽くまとめようかと思います

Delegateとは一体?

最初、Swiftを始めたときに、DelegateはSwift独自のものかと思っていましたが、違いました

デザインパターンDelegateだったのです!!

ということは、デザインパターンDelegateパターンを調べてみるとスッと入りやすいかもしれません

デザインパターンなんだと理解したことで少し抵抗がなくなりました

それで、Delegateとは何でしょうか?

Delegateは一般的に「移譲」と言われ、あるクラスから他のクラスに処理を任せるというものです

とは言われてもと思った方は多いはず(自分もそう)なので、サンプル元に説明しようかと思います

Delegateサンプル

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var TableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        TableView.delegate = self
        TableView.dataSource = self
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }
}

これはよく目の当たりにするサンプルだと思います

このViewControllerはTableViewを使うために、UITableViewDelegateとUITableViewDataSourceという2つのプロトコルに準拠しています

tableView(_ tableView, numberOfRowsInSection section)で1セクションの行数を指定しています

最初、これを見たときにどこから呼び出されているのかわからなくて悩んだ記憶があります

そこで、UITableViewのライブラリを見てみましょう

open class UITableView : UIScrollView, NSCoding, UIDataSourceTranslating {

    weak open var dataSource: UITableViewDataSource?

    weak open var delegate: UITableViewDelegate?

一部抜粋していますが、UITableViewDelegateUITableViewDataSourceの変数を持っています

Delegateの処理はUITableViewDataSourceで持っているので、そちらを見てみましょう

public protocol UITableViewDataSource : NSObjectProtocol {

    @available(iOS 2.0, *)
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int

こちらも一部抜粋ですが、1セクションの行数を返す処理がプロトコルで定義されています

そして、最初のサンプルのTableView.delegate = selfTableView.dataSource = selfでTableViewのインスタンスDelegate先をViewControllerにしています

ここまでで下記のことが分かったと思います

・どのクラスでDelegateを使うか指定する

Delegateはprotocolで定義する

Delegateを使うと指定されたクラスではprotocolで指定された関数を記述する

サンプル②

もう一つサンプルを元に説明しようかと思います

まずはDelegateの定義です

protocol ButtonDelegate {

    // ボタンを非表示にする
    func hide()
}

clickメソッドでDelegateメソッドを実行します

これは処理を任せる側です

class Button {
    var delegate: ButtonDelegate? = nil
    func click() {
        if let dg = self.delegate {
            dg.hide()
        } else {
            print("何もしないっす")
        }
    }
}

そして処理を任される側の処理を書きます

class Button2: ButtonDelegate {
    func hide() {
        print("非表示だぜ!!")
    }
}

こうすることでhide()はButton2に任せる準備ができましたね

let button = Button()
 
let button2 = Button2()
button.delegate = button1
button.click()  // 非表示だぜ!!が出力される

button.delegate = button1でbuttonのDelegateをbutton1に任せるのでclickしたときに非表示だぜ!!が出力されます

他のクラスでも使うことがほとんどなのでprotocolで定義しているということがわかるかと思います

まだ?マークの人も多いかもしれません

開発していくうちに理解していけますので、いっぱい実装しましょう

また、Delegateを使わなくても開発できてしまうので、意識して実装すると理解が早まると思います

参照

【swift】イラストで分かる!具体的なDelegateの使い方。

Flutterプロジェクトを作成する

今回はFlutterのプロジェクトを作成する手順をご紹介しようかと思います

環境構築手順はこちら

複数手順あるので3パターンくらいにまとめようと思っています

コマンドで作成

プロジェクトを作りたい場所で下記コマンドを入力すればプロジェクト作成が可能です

$ flutter create test

デフォルトでは iOSネイティブコードの言語は Objective-CAndroidネイティブコードは Javaです

もし、それぞれ Swift、Kotlin に変更するなら、次のように-iオプション、-aオプションで言語を指定します。

$ flutter create -i swift -a kotlin test

Android Studioで作成

Android Studioで作成する場合はプロジェクトを作成するだけです

Android Studioを開いたらStart a new Flutter projectを選択します

f:id:kumaskun:20200517204814p:plain

そして、Flutter Applicationを選びます

f:id:kumaskun:20200517204913p:plain

次にアプリの設定を行います

f:id:kumaskun:20200517205311p:plain

Flutter SDK pathはFlutterをインストールしたパスを設定します。インストールしたらflutter配下にリソースが作られていると思うので、fllutterまでをパスにします

Project locationはプロジェクトを置くパスを設定します

最後にパッケージ名を設定します

KotlinとSwiftのサポートを含める場合はPlatform channel languageの部分にチェックを入れてください

f:id:kumaskun:20200517205934p:plain

ここでFinishを押すとプロジェクトが作成されます

VS Codeで作成

VS Codeで作成する場合はVS Codeを開いてCommand PaletteFlutter: New Projectを選択します

f:id:kumaskun:20200517212851p:plain

次にプロジェクト名を入力してEnterキーを押します

f:id:kumaskun:20200517213312p:plain

プロジェクトを保存するロケーションを聞くウインドウが立ち上がりますので、任意のフォルダを選択するとプロジェクトが作成されます

プロジェクトを作成したら下の方にデバイスを選択できる箇所があるので、動作確認用にデバイスを選択することができます(なければ作ることもできます)

f:id:kumaskun:20200517215501p:plain

Googleが携わっているだけあって、Android Studioではプロジェクトの作成はできても、Xcodeで作成はできないみたいです

参照

Flutter のプロジェクト作成方法

Flutter をはじめよう!

Swiftのenumについて

今回はSwiftでのenumについて書こうと思います

Javaenumとは違って、個人的に少しクセがあるのでまとめようと思います

一般的なenum

enum DominantArm {
    case left
    case right
}

let leftHand = DominantArm.left

caseで要素を定義します。let leftHand: DominantArm = .leftのように書くこともできます

値型enum

各要素に指定した型の値を割り当てることができます

各要素に割り当て可能なのは整数値リテラル浮動小数点数リテラル文字列リテラルのみで、その他の型を割り当てようとするとエラーになります

enum DominantArm: Int {
    case left = 1
    case right = 2
}

そして、値型enumrawValueを使うと要素に割り当てられた値を取得することができます

let leftHand: DominantArm = .left
signalBlue.rawValue //1

最初、rawValueってなんだと思ったらそういう仕様なんですね!!

また、値から列挙型を生成するイニシャライザが存在します

引数のrawValueに指定した値に対応する要素がない場合はnilが返ってきます。これは便利ですね

let leftHand: DominantArm? = DominantArm(rawValue: 1)
leftHand?.rawValue //1

//DominantArmには3に該当する要素が存在しないのでnilが返る
let unknownHand: DominantArm? = Signal(rawValue: 3)
unknownHand?.rawValue //nil

値型enumで要素に値を割り当てなかった場合、数値リテラルの場合は一番上の要素が0になり、下にいくつにれてインクリメントされた値になります

文字リテラルの場合は、要素と同じ名前の値になります。これも最初驚きました

関連型enum

関連型enumは要素に付属する値を定義することができます

関連型は要素そのものの値ではないので値型のようにサポートできる型の制限もありません

enum Language {
    case ja(String)
    case en(String)
}

var hello = Language.ja("こんにちは")
hello = .en("Hello")

このように使い分けができるので、可読性が上がります

enumのネスト

enumをネストして階層構造を明確にすることができます

enum Earth {
    enum Human {
        case left
        case right
    }
    case animal
}

let character = Earth.animal
// 左利きを取得
let weapon = Earth.Human.left

enumのメソッド

Swiftのenumはメソッドを定義することができます

enum Country {
    case japan
    case america

    func convertJapanese() -> String {
        switch self {
        case .japan:
            return "日本"
        case .america:
            return "アメリカ"
        }
    }
}

また、mutating属性をメソッドに定義することで列挙型自身を変更できます

下記は普段使うことのない一例ですww

enum Country: String {
    case japan = "日本"
    case america = "アメリカ"

    mutating func randomConvertJapanese() {
        switch self {
        case .japan:
            self = .america
        case .america:
            self = .japan
        }
    }
}

var country = Country.japan
country.randomConvertJapanese()
country.rawValue //"アメリカ"

ここまででまた一部です

プロパティとかまだまだありますので、別の機会に書きたいと思います

Swiftのenumはできることが多く、使う機会があると思うので、調べていきたいです

参照

Swiftのenumについてまとめてみる