yynsmk's tech blog

何でもできる=何にもできない

【20時間の魔法】エンジニアはいかにして速くスキルを高めるのか?

はじめに

「天才になりたい!!」と思って、最近はいくつか勉強法や読書法に関する本を読んでいます。
その中でも面白いなと思ったのが、ジョシュ・カウフマン著の「たいていのことは20時間で習得できる」という本です。
何かの中級者になるには20時間で十分っていう理論と実践について書かれた本でした。

ところで、IT業界の技術の変遷って激しいですよね?
常に新しい技術が生まれてそれを身につけていかねばならない。
そんなIT業界で生きる人たちにとって、この本の内容はぴったりだと思ったので、知見を共有したいと思います。

tl;dr

身につけたいスキルをいくつかの重要なサブスキルに分けて、それらを20時間みっちり練習しよう。

超速でスキルを身につけるには?

この本にはスキルを習得するための方法論が書かれているのですが、ここでの”習得”はマスターとは異なります。
一般的にスキルをマスターした人=プロになるには、何時間の練習をすればいいのでしょうか?
ベストセラーとなった以下の書籍では、プロになるのに1万時間の本気の練習が必要だと書かれています。

しかし、エンジニアがプロダクトに新たな技術を組み込む時にそんなに時間は取れません。
本質的な要素をつかんで、ある程度使いこなせるようになっていればいいんじゃないでしょうか?

数十時間の練習では、機械学習のスペシャリストやPHPのプロには到底なれないでしょう。
しかし、機械学習をサービスに組み込めるようになる、PHPを使ってWebサイトが作れるようになるといったことはできるはずです。

この”ある程度使いこなせるようになること”をこの本では”習得”と言い、それには20時間あれば十分だと言っているのです。
そして、そのスキル獲得法について、本の中で以下のように述べられています。

超速スキル獲得法とは、習得したいスキルをできるだけ小さなパーツに分解し、そのうち特に重要なものを見極め、まずそれを意識的に練習するというプロセスである。

このプロセスは大きく4つのステップに分けられます。

  1. スキルをサブスキルに分解する
  2. サブスキルについて学習する=インプット
  3. 集中して練習できる環境を作る
  4. サブスキルを練習する=アウトプット・実践

ここで重要なのが、学習とスキル獲得は異なるということです。
もう少し分かりやすく言うと、学習=理論でスキル獲得=実践であり、それらを同一視してはいけないということです。
サービス・アプリを作りたいなら、プログラミング言語について詳細に知る必要はありません。
サービス開発に支障が出ないレベルの知識があればいいのです。

とにかく練習しろ!

超速スキル獲得法を本当に超速に行うために、本の中ではいくつかコツが紹介されています。

  1. 身につけたいスキルを一つ選び、目標を明確に定めます。
  2. スキルを重要なサブスキルに分解します。
  3. サブスキルを一つずつ質より量と速さを重視して練習します。どんな壁が立ちはだかろうとも20時間は頑張ります。

身につけたいスキルをすでに身につけている人にフィードバックをもらいながら進めるのもいいそうです。

どれも当たり前のことかもしれませんが、できていない人が多いんじゃないでしょうか?
とにかく、どうやって練習すればいいんだろうか?とぐだぐだ考えず、早くそして速くたくさん練習することが重要ってことです。

事前準備を怠るな!

先程、とにかく早くたくさん練習することが大事と言ったのですが、何もやみくもに練習すればいいわけではありません。
練習の効果を最大限高めることが大切です。

そのためには、練習前のスキルについての学習・リサーチが重要です。
本やネット、目標とするスキルのプロから情報を収集し、重要なサブスキルを見極め、目標までのロードマップを明確にするのです。
最低限何ができれば、そのスキルを身につけたと言えるのか考えることもスキルの分解に役立ちます。

このように、練習の前に情報収集することで、無駄な努力をしなくても済むのです。
プログラミングで言うなら、Rubyの入門書を読んで配列の使い方や文字列の操作方法を一生懸命覚えるんじゃなくて、Rails使ってWebアプリ作る方法を勉強しろよ!ってことですね。

事前準備を怠り、完全独学オリジナルの方法でいくのはバカなので、先人の知恵を存分に活かしましょう。

おわりに

この本では、難しいテクニックについては言及していません。
どれも当たり前のことばかりです。
ただ、この当たり前を行動に移すのが意外と難しいので、習慣化するまで意識してやっていくことが大切だと思いました。

Factory Methodの最低限押さえておくべきポイントとは?

この記事は、GoFのデザインパターン23個のうちのFactory Methodについて解説したものです。

Factory Methodパターン以外の解説記事はこちらからご覧ください。

Factory Methodとは?

以前、以下の記事でTemplate Methodパターンについて解説しました。
www.macky-studio.com

Template Methodパターンは処理の雛形となるテンプレートを用意しておき、具体的な処理内容はサブクラスが決めるというものでした。
このパターンをインスタンスの生成に応用したものがFactory Methodパターンです。
インスタンス生成時にメソッド1を行った後にメソッド2を必ず行うという流れは決まっているけれども、メソッド1及びメソッド2の内容はサブクラスで自由に決めていいよ!というパターンです。(メソッドの数は例として2つにしただけで、この数に決まりはありません)

Factory Methodを使ったプログラム例

例として、車を作る工場を考えます。
インスタンスを生成するフレームワーク側(抽象クラス)と実際の処理を決める具象クラスに別れている点がポイントです。

Product.java
フレームワーク側の抽象クラス。

package framework;

public abstract class Product {
    public abstract void use();
}

Factory.java
フレームワーク側の抽象クラス。
Productインスタンスを生成するTemplate Methodを用意しています。
「製品を生成し、登録する」という枠組みだけが決まっています。

package framework;

public abstract class Factory {
    public final Product create(String owner) {
        Product p = createProduct(owner);
        registerProduct(p);
        return p;
    }
    protected abstract Product createProduct(String owner);
    protected abstract void registerProduct(Product product);
}

Car.java
Productクラスを実装したクラス。

package card;
import framework.*;

public class Car extends Product {
    private String owner;
    private int number;
    
    Car(String owner, int number) {
        System.out.println(owner + "の車を作ります。");
        this.owner = owner;
        this.number = number;
    }
    public void use() {
        System.out.println(owner + "の車を運転します。");
    }
    public String getOwner() {
        return owner;
    }
    public int getNumber() {
        return number;
    }
}

CarFactory.java
Factoryクラスを実装したクラス。

package card;
import framework.*;
import java.util.*;

public class CarFactory extends Factory {
    private HashMap database = new HashMap();
    private int number = 0;
    
    protected synchronized Product createProduct(String owner) {
        return new Car(owner, number++);
    }
    protected void registerProduct(Product product) {
        Car car = (Car)product;
        database.put(new Integer(car.getNumber()), car.getOwner());
    }
    public HashMap getDatabase() {
        return database;
    }
}

Main.java
作成したプログラムの動作を確かめるプログラムです。

public class Main {
    public static void main(String[] args) throws Exception {
        Factory factory = new CarFactory();
        Product car1 = factory.create("Tom");
        Product car2 = factory.create("Mike");
        car1.use();
        car2.use();
    }
}

実行結果

Tomの車を作ります。
Mikeの車を作ります。
Tomの車を運転します。
Mikeの車を運転します。

最低限押さえておくべきポイント

先程も言いましたが、インスタンスを生成する流れを定義するフレームワーク側とそれを利用する側に分かれている点がポイントです。
そうすることで、具体的なクラス名はサブクラスで決めることができ、別の製品を作る場合にもフレームワークを利用して簡単にインスタンスを生成することができます。

まとめ

Factory Methodパターンを用いることで、インスタンスの生成方法をフレームワークとして提供できるため、プログラムの再利用性が高まります。
複数のサービスや製品を展開していて、会員情報の作成プロセスが共通している場合や、製品の作成プロセスが共通している場合によく使われるのかなぁと思いました!
ただ、実際にパターンを適用するには練習が必要ですね。。。

参考書籍

入門とだけあって、非常に分かりやすいです。
Javaの基本的な知識があれば割とすらすら読み進められるのでおすすめです。

【Swift】JSQMessagesViewController後継のMessageKitでのチャット画面の作成方法

はじめに

Swiftでチャットアプリを作ろうと思い、どんなライブラリが主流なのかな?と思って調べていると、これまではJSQMessagesViewControllerが一般的だったらしいですが、すでに開発が終了しており、現在は非推奨となっていることが分かりました。
そして、JSQMessagesViewControllerの後継?のMessageKitなるものを見つけました。

使い方を解説してくれているサイトが少なかったのですが、以下のサイトが非常に分かりやすかったです。

www.raywenderlich.com

ちなみに全部英語です(笑)
英語がある程度できれば問題なく読めますが、英語ってだけで挫折しそう!って方もいると思うので、そんな方たちのためにこの記事ではMessageKitの使い方をかみ砕いて日本語で説明していこうと思います!
Firebase Tutorial: Real-time ChatMessageKitのサンプルプロジェクトを合わせて見るとより理解できると思います。

環境

macOS Mojave 10.14.3, XCode 10.2.1, iOS 12.2, Swift 4.2.1, MessageKit 2.0.0

MessageKitのインストール

以下の手順でCocoaPodsを使用してライブラリをインストールします。

$ cd プロジェクト名
$ pod init
$ vim Podfile
$ pod install

Podfileには以下の内容を記述します。

# Pods for FirebaseChat
pod 'MessageKit'
pod 'MessageInputBar' // ver2.0.0から必要

スポンサーリンク

MessageKitの使い方

MessageKitはUICollectionViewControllerの強化版みたいな感じなので、UICollectionViewControllerの使い方が分かっていれば割と簡単です。
MessagesViewControllerクラスを継承するだけでチャット画面を作れます。
後は必要なプロトコルを実装していくって感じです。
メッセージはMessageTypeに準拠したMessageという構造体を自分で用意する必要があるんですが、これはMessageKitのサンプルプロジェクト内のMockMessageを利用すれば問題ありません。

class ChatViewController: MessagesViewController {

    private var messages: [Message] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        messagesCollectionView.messagesDataSource = self
        messagesCollectionView.messageCellDelegate = self
        messagesCollectionView.messagesLayoutDelegate = self
        messagesCollectionView.messagesDisplayDelegate = self
        messagesCollectionView.messageCellDelegate = self
        messageInputBar.delegate = self
    }
}

メッセージの送信やアバターの設定といったカスタマイズは以下の6つのプロトコルを使って行います。

  • MessagesDataSource
  • MessageInputBarDelegate
  • MessagesDisplayDelegate
  • MessagesLayoutDelegate
  • MessageCellDelegate
  • MessageLabelDelegate

それぞれについて簡単に説明していきます。 設定項目は多いですが、テキストメッセージの送受信さえとりあえずできればOKという場合は、 MessagesDataSourceMessageInputBarDelegateのみ実装すれば大丈夫です。

MessagesDataSource

メッセージの送信者や画面に表示するメッセージの数などの設定ができます。
全部で9つのメソッドがありますが、必ず実装しないといけないのはcurrentSendermessageForItem, numberOfSectionsの3つです。
~AttributedTextではメッセージやセルの上下に表示する文字列を設定できます。

extension ChatViewController: MessagesDataSource {

    // idとdisplayNameをプロパティに持つ構造体Senderを返す
    func currentSender() -> Sender {
       return Sender(id: "1", displayName: "Tom")
    }

    func numberOfSections(in messagesCollectionView: MessagesCollectionView) -> Int {
        return messages.count
    }

    func messageForItem(at indexPath: IndexPath, 
        in messagesCollectionView: MessagesCollectionView) -> MessageType {

        return messages[indexPath.section]
    }

    func cellTopLabelAttributedText(for message: MessageType, 
        at indexPath: IndexPath) -> NSAttributedString? {

        let name = message.sender.displayName
        return NSAttributedString(
            string: name,
            attributes: [
                .font: UIFont.preferredFont(forTextStyle: .caption1),
                .foregroundColor: UIColor(white: 0.3, alpha: 1)
            ]
        )
    }
}

MessageInputBarDelegate

メッセージ入力バーに関する処理を実装するものです。
ほぼ確実に実装することになるのは、送信ボタンを押した時の処理を表すmessageInputBarメソッドだと思います。
以下の例は、送信ボタンを押すと、入力された文字列からMessageオブジェクトを作成して、メッセージリストに追加し、メッセージ入力バーを空にして、画面の下までスクロールするというものです。
insertMessage(message)もしくは、それに値するメソッドは自分で作る必要があります。
そのメソッドの中でメッセージデータをローカルに保存するなり、データベースに保存するなりします。 firebaseを利用する場合の処理と、チャット画面全体をリロードして、送信したメッセージを表示する処理もここで行います。

extension ChatViewController: MessageInputBarDelegate {
    
    func messageInputBar(_ inputBar: MessageInputBar, didPressSendButtonWith text: String) {      
        let message = Message(text: text, sender: currentSender(), messageId: UUID().uuidString, date: Date())
        insertMessage(message)
        inputBar.inputTextView.text = String()
        messagesCollectionView.scrollToBottom(animated: true)
    }
    
}

MessagesDisplayDelegate

メッセージの表示形式やデザインを決めるものです。
テキストメッセージのみに反映される設定や全てのメッセージに反映される設定等いろいろあるので、MessageKitのサンプルプロジェクトを参考にするといいと思います。
以下の例では、相手のメッセージが自分のメッセージかに応じてメッセージの背景色を変えて、メッセージスタイルを吹き出しにしています。
isFromCurrentSender()がそのメッセージが自分が送信したのもかどうかを判定するメソッドです。
.primary.incomingMessageは自作の色なので、ここは各自好きな色にしてください。

extension ChatViewController: MessagesDisplayDelegate {
  
    func backgroundColor(for message: MessageType, at indexPath: IndexPath, 
        in messagesCollectionView: MessagesCollectionView) -> UIColor {

        return isFromCurrentSender(message: message) ? .primary : .incomingMessage
    }

    func messageStyle(for message: MessageType, at indexPath: IndexPath, 
        in messagesCollectionView: MessagesCollectionView) -> MessageStyle {

        let corner: MessageStyle.TailCorner = isFromCurrentSender(message: message) ? .bottomRight : .bottomLeft
        return .bubbleTail(corner, .curved)
    }
}

MessagesLayoutDelegate

このプロトコルはメッセージ周辺のラベルのレイアウトを決めるものです。
メッセージ周辺のラベルというのは、メッセージ付近に表示される時間や既読、日付といったものです。
それらの位置や大きさはここで設定できます。
メッセージの下に余白を作るといったこともここで行います。

extension ChatViewController: MessagesLayoutDelegate {
    
    func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath, 
        in messagesCollectionView: MessagesCollectionView) -> CGFloat {
        return 18
    }
    
    func messageTopLabelHeight(for message: MessageType, at indexPath: IndexPath, 
        in messagesCollectionView: MessagesCollectionView) -> CGFloat {
        return 20
    }
    
    func messageBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, 
        in messagesCollectionView: MessagesCollectionView) -> CGFloat {
        return 16
    }
}

MessageCellDelegate

チャット画面で様々なものをタップした時の処理を実装できます。
以下は、アバター(自分のプロフ画像が表示される所)とメッセージがタップされた時の処理の例です。
アバターとメッセージをタップするとコンソールにそれぞれ文字列が出力されます。

extension ChatViewController: MessageCellDelegate {
    
    func didTapAvatar(in cell: MessageCollectionViewCell) {
        print("Avatar tapped")
    }
    
    func didTapMessage(in cell: MessageCollectionViewCell) {
        print("Message tapped")
    }
    
}

MessageLabelDelegate

メッセージ中の電話番号や住所、URLなどをタップした時の処理を実装できます。
URLをタップしたらブラウザを開く、住所をタップしたらマップを起動するといった処理はここで実装します。
以下は例なので、コンソールに文字列を出力するだけです。

extension ChatViewController: MessageLabelDelegate {
    
    func didSelectAddress(_ addressComponents: [String: String]) {
        print("Address Selected: \(addressComponents)")
    }
    
    func didSelectPhoneNumber(_ phoneNumber: String) {
        print("Phone Number Selected: \(phoneNumber)")
    }
    
    func didSelectURL(_ url: URL) {
        print("URL Selected: \(url)")
    }
    
}

おわりに

さらに詳しく知りたい方はMessageKit Referenceをご覧ください。
今回の内容だけだとチャットアプリにはならないので、次はFirebaseとも組み合わせた実装例についても記事を書きたいと思います!

Template Methodのプログラム例およびメリットとデメリット

この記事は、GoFのデザインパターン23個のうちのTemplate Methodについて解説したものです。

Template Methodパターン以外の解説記事はこちらからどうぞ。

Template Methodとは?

Template Methodパターンとは、メソッドをテンプレートとして用意しておくものです。
テンプレートは処理の枠組みです。
スーパークラスのメソッドで処理の大枠だけを決めておいて、具体的な内容はサブクラスで決めます。
日常生活でもテンプレって言葉を使うときがあると思います。
メールだと、事前に用意された定型文の組み合わせであるテンプレを自分の都合に合わせてカスタマイズしますよね?
それと同じことをプログラムでもやります。

Template Methodを使ったプログラム例

飲み物を自動で作ってくれる機械を例にとってプログラムを作ってみました。

DrinkMaker.java
粉を入れる→液体を入れる→提供するという処理の流れ=テンプレートを定義するクラスです。
ここで、putPowderputLiquidは抽象メソッドなので、細かい処理内容はサブクラスによって変わります。

public abstract class DrinkMaker {
    public abstract void putPowder();
    public abstract void putLiquid();
    private void serve() {
        System.out.println("完成です。");
    }
    // テンプレートメソッド(finalを付けて、サブクラスで変更されないようにする)
    public final void make() {
        putPowder();
        putLiquid();
        serve();
    }
}

CoffeeMaker.java
コーヒーを作るサブクラスです。
スーパークラスの抽象メソッドを全て実装しています。

public class CoffeeMaker extends DrinkMaker {
    public void putPowder() {
        System.out.println("インスタントコーヒーを入れます。");
    }
    public void putLiquid() {
       System.out.println("お湯を注ぎます。");
    }
}

MilkteaMaker.java
ミルクティーを作るサブクラスです。
スーパークラスの抽象メソッドを全て実装しています。

public class MilkteaMaker extends DrinkMaker {
    public void putPowder() {
        System.out.println("インスタント紅茶を入れます。");
    }
    public void putLiquid() {
       System.out.println("ミルクとお湯を注ぎます");
    }
}

Main.java
作成したプログラムの動作を確かめるプログラムです。
サブクラスのインスタンスをスーパークラスのインスタンスに格納しているのがポイントです→LSP

public class Main {
    public static void main(String[] args) throws Exception {
        DrinkMaker dm1 = new CoffeeMaker();
        DrinkMaker dm2 = new MilkteaMaker();
        dm1.make();
        dm2.make();
    }
}

Template Methodのメリットとデメリット

Template Methodパターンでは、スーパークラスで処理の大枠、つまりアルゴリズムを決めているので、サブクラスでは細かな実装をするだけで済みます。
これは、同じようなアルゴリズムを持つクラスを複数作成する時に役立つのが分かると思います。
アルゴリズム自体にバグや変更が発生した時もスーパークラスのみを見ればいいので、修正が楽です。
一方、テンプレートを詳細に作りすぎると、サブクラスでの拡張性が低くなってしまいます。
何が共通化できるかをよく見極める必要があります。

参考書籍

入門とだけあって、非常に分かりやすいです。
Javaの基本的な知識があれば割とすらすら読み進められるのでおすすめです。

IteratorパターンとAdapterパターンのメリットとは?

はじめに

とあるエンジニアの方からデザインパターンは知っておいた方がいいと聞いたので勉強し始めました。
使用する教材は有名な「Java言語で学ぶデザインパターン入門」です。

この本は第一部から第十部まであるのですが、この記事では第一部についてまとめました。
第一部で登場するデザインパターンは以下の2つです。

  • Iteratorパターン

  • Adapterパターン

デザインパターンとは?

デザインパターンとはGoF(有名な4人らしい)が考えた23個の設計パターンのことです。
デザインパターンに当てはめることで、再利用しやすいプログラムを作れますし、知っているだけでもプログラムの構造について議論する時に役立ちます。

Iteratorパターン

Iteratorパターンはループ変数の働きを抽象化したもので、集合体の各要素を順に指し示していくパターンです。

読んでいる途中は、イテレータをわざわざ実装する意味があるのか?for文で良くない?
と思っていたのですが、読み終えてそのメリットが分かりました。

Iteratorパターンのメリット

for文を使う場合

数え上げる側が集合体クラスの実装(配列なのか、ベクトルなのかなど)について知っておく必要があります。

イテレータを使う場合

集合体クラスに対応したイテレータクラスを用意しておくと、そのイテレータインスタンスを使って数え上げられます。
数え上げる側は集合体クラスの実装については知らなくても問題ありません。

つまり!実装に依存しないプログラムを組める=クラスを再利用しやすい、 これがイテレータを実装するメリットです。

Adapterパターン

AdapterはWrapperともいい、「すでにあるプログラム」と「必要としている機能」とのずれを埋めるようなデザインパターンです。
すでにあるプログラムを利用して、必要としている機能を実現します。
Adapterパターンの実装には以下の2種類があります。

  • 継承を使う(すでにあるプログラムを継承してそのメソッドを使う)

  • 委譲を使う(すでにあるプログラムのインスタンスを作成し、インスタンスを通してメソッドを使う)

継承使ったAdapterパターンのプログラム

継承を使う方のサンプルプログラムを作ってみました。(デザインパターンを学習するには自分で例を考えてみるのが良いらしいです)

Adaptee.java
すでにあるプログラム。
引数の数値を標準出力する機能を持つ。

public class Adaptee {
    public void printNum(int num) {
        System.out.println(num);
    }
}

Target.java
必要としているインタフェース・機能。
引数の文字列を数値に変えて標準出力する機能を持つ。

public interface Target {
    public abstract void printStringToNum(String num);
}

Adapter.java
すでにあるプログラムと必要としている機能とのずれを埋めるアダプター。
すでにある機能printNum()を使って、必要な機能printStringToNum()を実現する。

public class Adapter extends Adaptee implements Target {
    public Adapter() {
        super();
    }
    public void printStringToNum(String num) {
        printNum(Integer.parseInt(num));
    }
}

Main.java
新たなインタフェースを使うクラス。

public class Main {
    public static void main(String[] args) {
       Target target = new Adapter();
       target.printStringToNum("12");
    }
}

この例は簡単で、すでに数値を表示するプログラムAdaptee.javaがあって、それには一切手を加えず、文字列を数値として表示するような機能Target.javaを使えるようにするものです。
ポイントはprintStringToNum()の中で、スーパークラスのprintNum()を呼び出している点です。 また、Mainクラスの中でAdapterインスタンスをTarget型の変数に格納して、Targetのインタフェースを使っている点も要チェックです。

Adapterパターンのメリット

Adapterパターンは、すでにテスト済みのプログラムがあり、それを新たなAPIに対応させる場合に使われます。
一見すでにあるプログラムを修正する方が簡単そうに見えますが、そうするとせっかくテスト済みだったプログラムをもう一回テストしないといけなくなって面倒です。
新たにAdapterを作った場合はテストするのはAdapterのみで済むのでバグも発見しやすくなります。

おわりに

第一部を読んでみて、デザインパターンはクラスの再利用化を促進するためにあって、そのためには抽象クラスやインタフェースを積極的に使っていかんとだめなのねって感じです。
第二部以降も読み進みてまとめていこうと思います。

【Swift】画面遷移と値受け渡しの方法まとめ

画面遷移の方法

Swiftには画面遷移の方法がいくつかあると思います。
自分の頭を整理するためにもここでまとめておきます。

Storyboardのみで実装する

これは最も簡単な方法で、ボタンを遷移先のViewCntrollerをつなげて、Segueの種類を選ぶだけで完了です。

performSegueを使う

  1. ViewController同士をSegueでつなぐ
  2. このSegueにはStoryboard Segue→Identifierを設定する(nextとか) f:id:b_murabito:20190424015659p:plain:w250
  3. 遷移したいタイミングでperformSegueメソッドを呼び出す
self.performSegue(withIdentifier: <SegueのIdentifier>, sender: nil)

ViewControllerにStoryboard IDを設定する

  1. 遷移先のViewController(NextViewControllerとする)のIndetify→Storyboard IDを設定する(nextとか)
    f:id:b_murabito:20190424015522p:plain:w250
  2. instantiateViewControllerメソッドで遷移先のViewControllerを特定する
  3. presentメソッドを呼び出す
let next = self.storyboard?.instantiateViewController(withIdentifier: <Storyboard ID>)
self.present(next, animated: true, completion: nil)

Navigation Controllerを使う

  1. Editor→Embed InからNavigation Controllerを追加する
  2. 遷移先のViewController(NextViewControllerとする)のIndetify→Storyboard IDを設定する(nextとか)
  3. 以下のコードで画面遷移する
// 進むとき
let next = self.storyboard?.instantiateViewController(withIdentifier: <Storyboard ID>)
self.navigationController?.pushViewController(next, animated: true)

// 戻るとき
_ = navigationController?.popViewController(animated: false)

値受け渡しとの組み合わせ

画面遷移と合わせてよく使う値の受け渡しの方法にもついてもまとめておきます。

performSegueとprepareを使う

performSegueメソッドの内部で呼び出されるprepareメソッドをオーバーライドすることで値を受け渡すことができます。

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let next = segue.destination as! NextViewController
    next.<プロパティ> = <受け渡す値>
}

presentと値受け渡し

遷移先のViewControllerのインスタンスを取得し、そのインスタンスのプロパティを設定することで値を受け渡すことができます。

let next = self.storyboard?.instantiateViewController(withIdentifier: <Storyboard ID>) as! NextViewController
next.<プロパティ> = <受け渡す値>
self.present(next, animated: true, completion: nil)

クロージャを使っても上と同等のことができます。

let next = self.storyboard?.instantiateViewController(withIdentifier: <Storyboard ID>) as! NextViewController
self.present(next!,animated: true, completion: {
    next.<プロパティ> = <受け渡す値>
})

頭がいい感じに整理できました〜!

参考資料

Swift初心者の方は一回読んでみると良いと思います。
これを読めば一通りSwiftとアプリ開発のノリが理解できるのでおすすめです。

ネットワークの環境設定をスクリプトで自動で変更できるようにするには?

これまで、ネットワークの環境設定(自動とか固定IPとか)を手動で変更していました。
今回は、自分の勉強も兼ねてそれをシェルスクリプトで自動化してみます。

以下の記事が非常に参考になりました。ありがとうございます🙇‍♂️ qiita.com

作成したコードはGitHubにあげました。
github.com

コマンドによる設定の変更

macのネットワーク設定は、GUIの他に以下の2つのコマンドを使ってもできます。

  • networksetup
  • scselect

scselectよりもnetworksetupのほうができることの幅が広いです。
設定の変更自体はどちらのコマンドでも可能です。

networksetup

インタフェースにつながっているネットワークの情報を表示

$ networksetup -getairportnetwork en0
Current Wi-Fi Network: <SSID>

現在のネットワーク設定を表示

$ networksetup -getcurrentlocation
Automatic

ネットワーク設定の変更

$ networksetup -switchtolocation <変更したい設定>
found it! %

Wi-FiのON/OFFの表示

$ networksetup -getairportpower en0
Wi-Fi Power (en0): On

scselect

ネットワークの設定一覧を表示(*が現在の設定)

$ scselect 
Defined sets include: (* == current set)
 * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (Automatic)
   xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (University)

ネットワーク設定の変更

$ scselect Automatic
CurrentSet updated to  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (Automatic)

自動化スクリプトの作成

スクリプトは基本的に参考サイトのものを使えば問題ないのですが、一部自分なりに変更を加えました。
おおまかな処理の流れはこんな感じです。

  1. Wi-FiがOFFになっていたらONに変える
  2. SSIDの値(ネットワーク名)に応じて、指定の設定に変更する

以下が完成したスクリプトです。

#!/bin/sh
ADAPTER="<インタフェース名>"

# Wi-FiがOFFならONに変える
airportpower=`networksetup -getairportpower ${ADAPTER} | awk -F ': ' '{print $2}'`
if test ${airportpower} = 'Off'; then
  networksetup -setairportpower ${airportpower} on
  sleep 3
fi

# 設定とSSIDを引数にして設定を変更してデスクトップに通知するメソッド
switch_location() {
    currentlocation=`networksetup -getcurrentlocation`
    if test $currentlocation = $1; then
        return
    fi
    scselect ${1}
    # networksetup -switchtolocation ${1}
    osascript -e 'display notification "'"ネットワーク環境を「${1}」へ変更しました。"'" with title "'"${0##*/}"'" subtitle "'"${2}"'"'
}

# 取得したSSIDの値に応じて設定を振り分ける
SSID=`networksetup -getairportnetwork ${ADAPTER} | awk -F ': ' '{print $2}'`
case "$SSID" in
  "<SSID>")
    switch_location "<変更したい設定>" ${SSID}
    ;;
  *)
    switch_location "Automatic" ${SSID}
    ;;
esac

デスクトップ通知にはAppleScriptを使います。
以下のコードで通知が出せます。

display notification "本文" with title "タイトル" subtitle "サブタイトル" sound name "サウンド名"

AppleScriptをターミナルまたはシェルスクリプトから動かすには、スクリプトを文字列にしてosascript -eを付けます。

SSIDの変更を検知してスクリプトを自動実行

先程のスクリプトを自動実行するには、Launch Agentsを利用します。
Launch Agentsにスクリプトを登録することで何らかのイベント発生時に自動実行することが可能です。

ちなみに、SSIDの変更検知は/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plistを監視することで分かります。
ネットワーク関連の何かしらに変化があるとこのファイルも変更されるみたいです。

Launch Agentsについては以下のサイトが分かりやすかったです。

takuya-1st.hatenablog.jp

このサイトの通りに、plistを作って、シンタックスを確認して、ロードして、一覧で確認できれば登録完了です!

※追記:plistは~/Library/LaunchAgentsに置く必要があります。

plistの作成

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>Lauch Agentへの登録名</string>
  <key>ProgramArguments</key>
  <array>
    <string>実行したいスクリプトの絶対パス</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>WatchPaths</key>
  <array>
    <string>/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist</string>
  </array>
</dict>
</plist>

シンタックスの確認

$ plutil -lint <path to plist>
<path to plist>: OK

plistのロード

$ launchctl load <path to plist>

登録の確認

$ launchctl list | grep <plistのラベル>
-       0       <path to plist>

おわりに

無事にできました〜!
個人的に非常に学びが多くてやった甲斐がありました。
また、何かおもしろそうなことがあればやってみようと思います。

【Swift】具体例でわかりやすいプロトコル解説

どうも。
今回は、Swiftで重要なプロトコルについて具体例を出しつつ解説していこうと思います。

プロトコルとは?

プロトコル(protocol)という言葉の辞書的な意味は「条約」とか「規約」です。
Swiftにおいては抽象化した約束事を表します。
言葉本来の意味に加えて抽象化という概念が入るのがポイントです。

現実問題で考えてみる

例として、選挙を考えます。
選挙では各立候補者は公約つまり、「日本を良くするための約束事」を 言います。
また、昨今問題となっている少子化問題を必ず解決しなければならないとします。

ここで、少子化問題の解決方法は各立候補が決めることです。
そのため、必須であっても具体的な内容まではあらかじめ決めておけませんよね。

つまり、必ず守って欲しい内容は抽象的な約束事として用意しておいて、細かい内容は約束事を守る側に決めさせるというのがSwiftにおけるプロトコルです。

プロトコルの使い方

上の例を踏まえてプロトコルの使い方をソースコードで見ていきましょう。

// プロトコルの宣言
protocol PublicCommitment {
    func stopDecliningBirthRates()
}

// プロトコルに準拠した麻生クラス
class Asou: PublicCommitment {
    func stopDecliningBirthRates() {
        print("子育ての補助金を増やします")
    }
}

// プロトコルに準拠した安倍クラス
class Abe: PublicCommitment {
    func stopDecliningBirthRates() {
        print("育メン制度を整えます")
    }
}

プロトコルは基本的にクラスと同じように定義・継承して使います。
クラスと違うのは主に次の2点です。

  • プロトコルのメソッドは空

  • プロトコルは多重継承が可能

プロトコルのメソッドが空なのは、上で説明したようにそのプロトコルに準拠した各クラスで具体的な処理内容を記述するためです。
逆に、プロトコルに準拠したのにメソッドを実装していないとエラーが発生します。

iOSアプリ開発におけるプロトコルの具体例

アプリの開発中に独自のプロトコルを作成することはあまりありません。
どこで使われているのかというと、Appleが用意してくれているライブラリです。

例えば、TableViewを使いたいときは UITableViewDelegate, UITableViewDataSourceという2つのプロトコルに準拠して、以下の2つのtableViewメソッドを実装する必要があります。
「TableView使うんだったらこの2つは絶対必要だから実装してね」ってことでプロトコルにしているのだと思います。

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
    }

    tableView.reloadData()

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        return cell
    }
}

まとめ

  • プロトコルはiOSアプリ開発における必須の技術

  • プロトコルとは抽象化した約束事

  • プロトコルを準拠したクラス内でメソッドの実装をする

Swiftのプロトコルは、C++やJavaでいうインタフェースなんですが、個人的にはインタフェースよりも、ある機能を使うために実装しておかないといけない約束事って感じの方がわかりやすく思います。

また、プロトコルを勉強するにあたっては以下の書籍が非常にためになりました!

【Swift】UserDefaultsに自作クラスのデータを保存する方法(iOS12対応)

UserDefaultsを使っていてハマったことがあったのでメモ。

環境

Swift4.2.1、Xcode10.1

やりたいこと

UserDefaultsに自作クラスの配列を保存したい。

UserDefaultsとは?

データベースを使うことなく、iPhone上にデータを保存できるようにしてくれる機能です。 ただ、アプリをアンイストールするとデータは消えるので注意。 また、UserDefaultsで保存できる型は決まっており、以下の11個の型のみが利用できます。

Any, URL, [Any], [String: Any], String, [String], Data, Bool, Int, Float, Double

UserDefaultsの基本的な使い方

キーを用いて値を保存したり、参照したりします。 例えば、String型の配列を保存する場合は以下のようになります。

// 保存
UserDefaults.standard.set(["test1", "test2", "test3"], forKey: "tests")

// 参照
if let tests = UserDefaults.standard.stringArray(forKey: "tests") {
    print(tests)
}

UserDefaultsで自作クラスのデータを保存する方法

上で説明したように、UserDefaultsで保存できる型は決まっており、自作クラスは保存できません。 また、公式ドキュメントでも以下のような説明があります。

A default object must be a property list—that is, an instance of (or for collections, a combination of instances of) NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData.

つまり、自作クラスのデータを保存したいなら一度Data型に変換する必要があります。 今回は、Articleクラスのインスタンスの配列を保存する場合を例にとってサンプルコードを書きました。

保存したい自作クラス

// NSObjectを継承し、NSCodingを批准する必要があります
class Article: NSObject, NSCoding {
    var title = ""
    var date = ""

    init(_ title: String, _ date: String) {
        self.title = title
        self.date = date
    }

    // NSKeyedArchiverに呼び出されるシリアライズ処理(NSCodingで定義されている)
    func encode(with aCoder: NSCoder) {
        aCoder.encode(self.title, forKey: "title")
        aCoder.encode(self.date, forKey: "date")
    }

    // NSKeyedArchiverに呼び出されるデシリアライズ処理(NSCodingで定義されている)
    required init?(coder aDecoder: NSCoder) {
        title = aDecoder.decodeObject(forKey: "title") as! String
        date = aDecoder.decodeObject(forKey: "date") as! String
    }
}

iOS11までの保存と参照の処理

// 保存したい配列
var articles = [Article]()
let article = Article("Swift", "1月1日")
articles.append(article)

// 保存
let archivedData = NSKeyedArchiver.archivedData(withRootObject: articles)
UserDefaults.standard.set(archivedData, forKey: "articles")

// 参照
var articles = [Article]()
if let storedData = UserDefaults.standard.object(forKey: "articles") as? Data {
    if let unarchivedObject = NSKeyedUnarchiver.unarchiveObject(with: storedData) as? [Article] {
        articles = unarchivedObject
    }
}

iOS12での保存と参照の処理

// 保存したい配列
var articles = [Article]()
let article = Article("Swift", "1月1日")
articles.append(article)

// 保存
let archivedData = try! NSKeyedArchiver.archivedData(withRootObject: articles, requiringSecureCoding: false)
UserDefaults.standard.set(archivedData, forKey: "articles")

// 参照
var articles = [Article]()
if let storedData = UserDefaults.standard.object(forKey: "articles") as? Data {
    if let unarchivedObject = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(storedData) as? [Article] {
        articles = unarchivedObject
    }
}

NSKeyedUnarchiver.unarchivedObjectを使う方法もあるようなのですが、エラーが出てうまくいきませんでした。 誰か分かる方いればコメントください。

【専門家求む】規則性の感覚はいつ身につくの?

いきなりですが問題です。

 

    1, 2, ◯

 

この数字の並びを見て、◯に入る数字を答えてください 。

 

 

 

 

 

みなさん何と答えましたか?

多くの方が3、もしくは4と答えたんじゃないでしょうか?

 

ではさらに追加で問題です。

その数字を答えた根拠を述べてください。

 

 

 

 

 

どうでしょう?

上で3と答えた方は、「1, 2, って1ずつ増えとるやん。じゃあ◯に入るのは3や!」

上で4と答えた方は、「1, 2, って2倍ずつ増えとるやん。じゃあ◯に入るのは4や!」

このように考えた方がほとんどじゃないでしょうか。

 

 

 

 

 

そう答えた人たちにお聞きしたい。

”ずつ”ってなんですか?

+1 or ×2されたのは”1回”だけですよ?

”ずつ”って最低二つは同じものが続かないと成り立たないですよね?

 

煽ってすみません。

今回の本題は、どういうわけか我々は規則性があるとは言えない数列に対して、勝手に規則性を見出してしまう癖があるってことです。(ちなみに最初の問題の筆者的解答は「解は無限にある」です)

でも、それってなぜなんでしょうか?

 

この理由を調査するにあたり、2つの説を考えました。

  1. 超頭良い or 超バカな人は固定概念に囚われずに「解は無限にある」と答えてくれる。
  2. まだ算数を習っていない子供はでたらめな数字を言う。

データ数が明らかに足りてませんが、一応2つの検証結果を報告します。

1つ目は京大数学科の博士課程の人に協力してもらいました。

結果は、“解は無限にあるってことに気づいた上で”いろんな角度からいろんな答えをくれました。

バカな人での検証はできてません。

 

2つ目は、友人に親戚の幼稚園児の子(若干数字には触れている)に聞いてもらいました。

結果はなんと!3って答えたそうです。

さらに驚きなのが、6, 8, ◯って問題も出してら10と答えたことです。

1, 2, ◯ってきたらいくら小さい子でも3って答えしまいそうですが、6, 8, ◯でも規則性を無意識に見出してしまうんですね。

 

調査はここまでで、謎の解明には至りませんでした。

一体何歳の時点で規則性って考え方が身につくんでしょうか?

あと、規則性がないはずなのに規則性があると決め込んでしまうのはなんなんでしょうか?

誰か詳しい方教えてください🙇‍♂️

頼みます!!!!!

さくら味の正体はかわいい名前のあいつ!?

春が遠慮がちに攻めてきて、だんだんと暖かくなってきましたね。

 

ところでみなさん、春と言えば何を思い浮かべますか?

そうです、ですよね。

そんで、桜って食べ物の味にもありますよね?

さくら餅とかさくらフラペチーノだとか。

 

f:id:b_murabito:20190425015151j:plain

 

そこでふと疑問に思ったわけですよ。

このさくら味って何なのかと🤔

我々はあのきれいなピンク色の花を食べてるんですかね?

実際に咲いてる桜を取って食べたらさくら味がするんですかね?

 

気になったのでちょっと調べてみました。

訳もわからずうめーうめーと言っている人たちよ、これを見て勉強しやがってください。

 

クマリンという名の成分

どうやら桜の葉から抽出されるクマリンという成分がさくら味の正体らしい。

 

ただこのクマリン、生の桜の花や葉っぱにはo-クマリン酸配糖体という別の形で存在しているため、当然桜自体を食べてもさくら味はしません。

 

一般的には桜の葉を塩漬けにすることでクマリンを生成するそうです。

ちなみに以下がクマリンの構造式。

f:id:b_murabito:20190324170722p:plain

クマリンの構造式(Wikipediaより引用)

めっちゃシンプル!

高校の化学の教科書に載ってるんじゃないかレベルで簡単な式でびっくりですね。

 

おわりに

さくら味の正体は桜の葉から抽出されるクマリンという成分でした!

 

何がすごいって桜の葉を塩漬けにしようと考えた昔の人ですよね。

葉っぱなんか食べようと思わないですよね?

江戸時代?平安時代?どのくらい昔かは分かりませんが、それを塩漬けにしたってことは相当食べるものに困っていたんでしょうか?

それか興味本位で漬物的な感覚で漬けてみて、「なにこれ!?すごい良い匂いするやん!」ってなったのが始まりですかね。

 

誰か詳しい人いたら教えてください。

 

あ、ちなみに私はさくら味が苦手です。笑

祝ブログ開設

ブログを開設しました

私事ではありますが、ついにブログを開設しました!

わーぱちぱち👏

 

前々からやろうと思っていたのですが、ついつい先延ばしになっちゃっていました。。。

 

いやーこれは私にとっての偉大なる一歩。

例えるなら、大嫌いな牛乳を一気飲みする覚悟を決めるぐらいのもんですね。

  

 

さて、このブログでどんなことを投稿していくかですが、内容は大きく分けて以下の2つ。

 

  • IT技術(Webアプリ、iOSアプリ、ネットワーク等々)
  • 日常で気になったこと

 

この辺りを中心に今日から気が向いた時に更新していければと思います。

特に2つ目の日常で気になったことについては他の人の意見が聞きたいので、どんどんコメントしてください!

 

では、次回の投稿までしばしお待ちを。