渋谷で引きこもってる社長です

株式会社Chotchy代表取締役 / エンジニア

情報の流れを再構築する

情報の流れを再構築する。

これは僕が2年前からプロダクトや事業を作る際の核としていることです。元をたどると僕が書いてたカフェのブログの写真を引用されたところから始まります。基本自分が行ったことがあるカフェの記事 or プレスリリースの記事のみを書いていました。ところがある日僕の撮った写真が"キュレーションメディア"というものに引用されるようになりました。最初はなんだこれ!?ってなったのですが、だんだんこういうものなのだと諦めだしたのですが、一番腹立たしかったのが記事の内容が適当だったことです。

そして、何と言ってもそのまとめサイトの方がSEOが高いわけですよ!絶対僕の方が読者のためになる情報を自分の足で通って作り上げている、って思ってました。間違った営業時間を書いたりもう潰れたカフェの情報が載ってたりするメディアが自分のブログより上に来るのが非常に不快でした。そもそもで情報に愛がないんですよ。愛のない情報はユーザーのためなのか。ずっとずっとなんとかしたいと思っていました。

 

 

そして今、WEBにおけるメディアのあり方が問い直される時代にようやくなってきたなと思います。近いうち今の短絡的なキュレーションメディアのような記事は自然言語処理機械学習が施されたプログラムによって一瞬で大量生産されるようになるでしょう。そうなった時、愛のない情報を提供するだけのメディアは不要になってきます。

 

いまがチャンスだと思います。情報の流れを再構築する。

今一度、ユーザーにとって価値のある情報接触体験を作り出すということをじっくりと考えたいと思っています。

 

 

SwiftBondを使ってiOSでシンプルなリアクティブプログラミングを試す![TableView編]

弊社の学生iOSエンジニアのおーつか君がSwiftBondのサンプルコードを作ってくれたのでそれからちょいちょいSwiftBondを触ってるのですが、とてもいいですねこれ。Delegateを自分で作るなんてことも無くなりそうです。

何がいいかって完全にViewとControllerを切り離せるところにあると思いますね。

そんなSwiftBondでのReactive ProgrammingをUITableViewで試したサンプルコードを載せたいともいます。SwiftBondでもdidSelectなどのDelegateが使えるので見てみてください。

試しにTableViewではどうなってるのかを説明したいと思います。

ちなみにおーつか君のサンプルコードはこちらです!

github.com

 

Model

まずはDataの型を決めます。Webからデータを取得する前提になっています。

Data.swift 
import Foundation 
import SwiftyJSON 
import RealmSwift 
struct Data { 
  let thumbNail: String 
  let titleText: String 
  
  init(json: JSON) { 
    self.thumbNail = json["thumbnail"].stringValue 
    self.titleText = json["title"].stringValue 
  } 
}

次に、ViewModelです。ここで、Viewに渡す最終的な形まで持って行きます。


import Foundation 
import RealmSwift 
import UIKit 
import Bond 
class DataViewModel{ 
  ///このデータを監視対象にします。 
  internal var data = ObservableArray()
  
  //シングルトンパターン 
  static var sharedInstance: DataViewModel = { 
    return DataViewModel() 
  }() 
  private init() { } 
  
  //データの中身の更新処理 
  func update(){ 
    data.removeAll() 
    Alamofire.request(.GET, "https://hogeghoge.jp", parameters: ["category_id": category_id]) .responseJSON { response in 
      if response.result.isSuccess{
        let j = JSON(response.result.value!) 
        for i in 0..<j["list"].count { 
          let data = VideoData(json: j["list"][i]) 
          self.data.append(data) 
        } 
      } 
    } 
  } 
}

ViewController


import Foundation
import UIKit
import PopupController
import Bond

class DataViewController: UIViewController{
    
    var tableView: UITableView!
    //監視対象
    private var data = ObservableArray<ObservableArray>()
    private let dataModel = DataViewModel.sharedInstance
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.like = [likeModel.like]
        DataViewModel.sharedInstance.update()
        
        tableView = UITableView(frame: self.view.frame)
        tableView.registerClass(DataViewCell.self, forCellReuseIdentifier: "ViewCell")
        tableView.rowHeight = 86
        tableView.delegate = self
        self.view.addSubview(tableView)
        
        //データのバインディング
        data.bindTo(tableView) { indexPath, dataSources, tableView in
            let cell = tableView.dequeueReusableCellWithIdentifier("ViewCell", forIndexPath: indexPath) as! DataViewCell
            let dataSource = dataSources[indexPath.section][indexPath.row]
            cell.data = dataSource
            return cell
        }
    }

}

//選択時のDelegateはextensionで対応。
extension LikeViewController: UITableViewDelegate{
    
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        print(indexPath.section)
    }
}
    

View

TableViewCellにバインドするところです。


import Foundation
import SDWebImage
import UIKit

class DataViewCell: UITableViewCell {
    

    var thumbNail: UIImageView!
    var title: UILabel!
    
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }
    
    //データのバインドはdataのdidSetで行う
    var data: Data? {
        didSet{
            //nilチェック
            guard let data = self.data else { return }
            self.title.text = data.titleText
            self.thumbNail.sd_setImageWithURL(NSURL(string: data.thumbNail))
        }
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}
    

こんな感じです!もっと詳しく聞きたい人はinfo[@]chotchy.comまで!

ObserverPattern + RealmでモバイルMVCを試した

Swiftを始めて8か月ぐらいたちました。Swiftは割と現代のプログラミング言語のいいとこ取りをしてる感じがあり好きです。 そんな感じでそろそろSwift初心者から中級者に入っていけたらと思い、CocoaPodsでライブラリも公開しました。

github.com

是非使ってください!Starお願いします。

Observer PatternでModelを作る

最近、流行りのObserver PatternですがRXSwiftなどを使えば簡単にできます。が、僕なんかはそこまでやるほどでもないなというくらいの規模の時はこのやり方でいいと思います。 流れとしては、

  • シングルトンでModelを作成。Modelクラスのクラス変数にデータをもたせておく

  • ViewController(VC)がモデルからそのデータをゲッタによって取得

  • Modelクラスのデータに変更をVCから要求。変更が終わり次第変数の変更の通知を送る

  • VC側で変更を受け取りデータをModelから取得。UIに反映となります。

Model

import Foundation
import SwiftyJSON
import Alamofire

class KVOChannelFeedModel : NSObject {
    
    //外部クラスからの変更を許さないためprivateで宣言
    //監視の対象はdynamicで宣言。またその対象がNSObjectを継承している必要がある。
    private dynamic var channelList: [ChannelList] = []

    //singletonパターン
    class var sharedInstance: KVOChannelFeedModel {
        struct Singleton {
            static let instance: KVOChannelFeedModel = KVOChannelFeedModel()
        }
        return Singleton.instance
    }
    
    //privateであるためゲッタを設置。
    func getChannelData() -> [ChannelList]{
        return self.channelList
    }
    
    
    //変更。Post部分
    func channelUpdate(){
        let data = realm.objects(Channel)
        var list: [ChannelList] = []
        for r in data {
            let d = ChannelList()
            d.setFromRealmResult(r)
            list.append(d)
        }
        self.channelList = list
    }
    
}

Data構造

import Foundation
import RealmSwift

class ChannelList: NSObject{
    
    var channelId: Int!
    var name: String!
    var modified = NSDate()
    
    func setFromRealm(r: Channel){
        
        self.channelId = r.id
        self.name = r.name
        self.modified = r.modified
        
    }
    
}

ViewController側での処理

override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(true)
        //忘れず回収
        KVOChannelFeedModel.sharedInstance.removeObserver(self, forKeyPath: "channelList")
    }
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(true)
        UIApplication.sharedApplication().applicationIconBadgeNumber = 0
        
        //監視追加
        KVOChannelFeedModel.sharedInstance.addObserver(self, forKeyPath: "channelList", options: [.New], context: nil)
     }

override func viewDidLoad() {
        super.viewDidLoad()
        
        //Updateさせる部分。Modelで更新が始まる
        KVOChannelFeedModel.sharedInstance.channelUpdate()
}

//通知を受け取る場所
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        if keyPath == "feedChannelData"{

             //ゲッタを使って変更を取得
            self.channelData = KVOChannelFeedModel.sharedInstance.getChannelList()

            //tableViewなどはここでリロード
            self.tableView.reloadData()
        }
    }

といった感じです。

さっきから出てくるChannelという変数ですが、ここのアプリで使ったソースコードを使っています。まだまだβ版ですが読んだからにはダウンロードしてください。

また、Swiftで一緒に開発したいよ!って人、勉強したいよって人はここへ!

www.wantedly.com

Googleのtensorflowをちょろっと試してみた

Googleから機械学習オープンソースが公開されて話題になっていたのでとりあえず触ってみました。
今回は手書き文字の識別(MNIST)という超基本的な例が載っていたので日本語で解説しながら試していきたいと思います。
本家はここからどうぞ!
http://tensorflow.org/

インストールと準備

環境はPython2.7で、pipで簡単にダウンロードできます。pipをとりあえず最新のものにしましょう。

pip install --upgrade pip

そんで、インストールします。

sudo pip install https://storage.googleapis.com/tensorflow/mac/tensorflow-0.5.0-py2-none-any.whl

これで終了。勝手にnumpyも最新にされていた。

不安な人は

$ python
>>>import tensorflow

で確認しましょう。

ソースコード

MNISTは28×28ピクセルからなる手書き数字の画像のサンプルです。Googleは優しくもこの画像を自動で取得するプログラムも準備してくれています。ここからコピペでok。
tensorflow/g3doc/tutorials/mnist/input_data.py - tensorflow - Git at Google

今回はコスト関数にクロスエントロピー関数を使った誤差伝播法のニューラルネットワークです。では。

# -*- coding: utf-8 -*-
import tensorflow as tf
#MNISTの元データを読み込みます
import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
#入力層の初期化。28×28ピクセルのデータであるため、784の入力層を準備。
x = tf.placeholder("float", [None, 784])
#重みベクトルの初期化。入力層784、出力層10(0~9の数字を見分ける)
W = tf.Variable(tf.zeros([784,10]))
#出力に対する誤差項。
b = tf.Variable(tf.zeros([10]))
#確率にして表現するためソフトマックス関数をかけたものを最終的な出力表現とする。
y = tf.nn.softmax(tf.matmul(x,W) + b)
#正解データを格納する変数を宣言
y_ = tf.placeholder("float", [None,10])
#コスト関数。クロスエントロピー関数を使っています。言ってしまえば学習のときにどれだけ間違えたかを算出する関数です。
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
#そのコスト関数を最小化するように値を更新します。学習係数は0.01。誤差逆伝播法。
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
#初期化
init = tf.initialize_all_variables()
#セッションというものが計算の流れを管理してくれるらしい
sess = tf.Session()
sess.run(init)
#実際の学習。1000回学習します。
for i in range(1000):
  batch_xs, batch_ys = mnist.train.next_batch(100)
  sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
#出力層の中で一番大きく出力しているところをargmaxで返します。
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
print sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})

感想

このくらいのことは自分で直で書いてもできるので全然凄さが伝わらないのですが、Googleチュートリアルが丁寧すぎて、機械学習エバンジェリストにでもなるのかなっという印象。
まだ全然深くは見れていないけど、CNNとか入ってるみたいなので色々触ってみたいな、と思う反面、結局正解データとなるデータを持っていないと何の意味もないなぁとさらに深く感じましたね。
結局はプラットフォーム持ってないと全く意味がないんですよ。

こちらもお願いします。www.wantedly.com

ときめきを感じられる毎日を送るには

最近ときめいてますか?
って結構人に聞くの好きなんですけど。もちろん異性にときめくって意味ですけど、日常にときめきなんてかなり減ってきてるじゃないですか?

僕なんて中学生の時は3日に1回ぐらいはときめいてたと思うんですよ。今なんてほとんどときめかないですよ。

ただ、こう自分が過去どういう時にときめいてたかを考えてみると、その答えは「いちご100%」に書いてあったんですよ。

いちご100%って結局、閉じ込められるとかぶつかってパンツ見えちゃうとかそういうの多いんですけど、それって偶然の初体験=ハプンニングなんですよね。

思いもよらないタイミングで初体験と巡り合う、これがときめきだという結論に至りました。

ときめきって人の幸せの形の一つだと思うんですよ。

僕の持論の一つに、初対面の人と付き合うってことになるまでには、
初めて知り合う→二人で会う、のこの流れの中にときめきがないと行けないというのがありまして。

初めて知り合って、「あ、この人いいなー」って男が思って女の子に連絡して、二人で食事に行きます。この食事に行った時に何かしらのときめきがないと、もうダメだっていう理論です。

無難に食事して無難な話をして無難に帰って…
それで次また会いたいなって思うことはほとんどないですよね。

んじゃ、どうすればときめきが生まれるかなんですが、ちょっとしたものでいいと思います。

ハグした、足が密着した、手がち○ち○に当たった、満員電車で顔が近かった、目を見てかわいいって言われた、などなど。

つまりは、ときめきってリスクをとることでもしかしたら巡り会えるかもしれないものなのかなって。

ちょっとイスを近づけてみる、満員電車に便乗してくっついてみる、とか。

このときめきって異性だけではなく、サービスだったり、イベントだったり、商品だったり、全部に言えることだと思いますが。


広い意味でときめいた毎日を送りたいものですね。そしてときめきを提供していきたいですね。

とりあえず、いちご100%読み直します

みんながもっと「インターネット」を愛せば、世界から戦争をなくせるかもしれない

 

インターネットの力って偉大だと思うんですよ。だって、いままでは目の前の人としか会話できなかったのに、少なくとも"電話番号"っていうシリアルコードをゲットしないとその人と会話することができなかったのに、いまでは瞬間的に「他人」とつながることができるのですよ。

普通に生きていたら出会うことのない人と出会える可能性があって、そっから色んなものが生まれていく可能性があって。

僕なんか高校生の時、掲示板で知り合った人とバンド組んでましたからね。mixiで知り合った人とライブ見に行ったこともありますよ。その人がどんな生活を送って、どんな政治宗教思想であっても僕たちは共通の趣味を見つけて"友達"になれた時代です。いま思えばそんなインターネット無法地帯時代は楽しかったなぁ。

一方でやはり危険な一面もありました。児童買春や暴行事件など悪い話はどんどん増えて広まって。

そんななか、社会はそれを技術ではなく制度で対応しました。

 

最近は僕が好きだったインターネットじゃなくなってきているように思うんですよね。

 

若い子たちはLINEやTwitterで学校の友達と連絡とるじゃないですか。スマホが普及してインターネットとつながる時間が増えるにつれて、リアルとインターネットとの境目があいまいになっていって、学校の延長を家でやるためのツールとしてのインターネットになってしまっているかなと。

インターネットの時空間をぶっ飛ぶような無限に拡大していきそうなあのトリップした感覚を知らないのかもしれません。

 

難しい言い方をすると、インターネットでは私たちは複数アイデンティティを持つことを許されているのです。Twitterの複垢とかもそうですね。ジャニーズが好きな私、同性が好きな私...。

現実世界では抑圧されていた人格がインターネットのなかでは違うアイデンティティとして芽生え、新しい私としてコミュニケーションをとることができる。それがたとえ相手の顔が見えなくても。

アイデンティティ多様性というのは日常のなかではかなり表現しづらいものだと思うのですよ。同じ人と住んでいますし同じ地域に住んでいますから。同じ学校に通って同じ職場に通って。だからこそ、インターネットが活躍するのです。

 

憎しみはアイデンティティの対立から起こることが多いと思います。違う価値観、違う信条。価値観を受け入れるとか認めるってのはすごく難しいですし、できなくてもしょうがないと思うのです。ただ、対立する価値観を探すのではなく、共通項を探す営みの方がいいのではないでしょうか。

 

たとえ敵国だとしても一緒にスプラトゥーンをやった人をぶっ殺してやる、って思うかなっと。

 

 

 

 

 

 

 

Swift + RealmでAuto Increment付きのデータベースを作る

どうも。

最近、モバイルのエンジニアをかなり募集しているのでたまにはテックブログでも書くかなと。

 

じつは、6月末くらいからSwiftを初めてだいたい2ヶ月なのですが、だいぶ慣れてきました。そもそも、日本語のリファレンスやブログが少ないので英語が読めない保守的な人間が多い日本ではまだまだSwiftは普及しなさそうですが、わりとobject-cよりもすっきりしている印象を受けます。

 

さらに、モバイルのデータベースを使うなら圧倒的にRealmをお勧めします。SQLが慣れているからとかいう理由でSQLiteを使うと思うのですが、断然Realmの方がすっきりしております。

日本語のリファレンスもあります。

ダウンロード等はこちらから。

Realm is a mobile database: a replacement for SQLite & Core Data

 

ただ、一つRealmにはAutoIncrementの機能が付いてません。そこがちょっとめんどい。

なので僕は普通にsort()で逆順にして最大id + 1とすることでAutoIncrementを実現してます。

            let realm = Realm()
            var max = realm.objects(Message).sorted("id",ascending:false)
            //保存する
            let m = Message()
            m.id = max[0].id + 1
            ...........
            realm.write {
                      realm.add(m, update: true)
            }

idはprimary keyに設定しておきます。

import RealmSwift

class Message: Object {
    dynamic var id = 0
    dynamic var user_id = 0
    dynamic var content = ""
    dynamic var isread = 0
    dynamic var created: NSDate!
    
    //primary keyに設定します
    override static func primaryKey() -> String? {
        return "id"
    }
}

ちなみに、SwiftMysqlのtimestampからNSDateに変換するには、

    
                        //APIから受け取ったdataをSwiftyJSONで変換
                        let json = JSON(data)
                  var dateAsString: String = json["post"][i]["created"].stringValue
                        var formatter: NSDateFormatter = NSDateFormatter()
                        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
                        var date: NSDate = formatter.dateFromString(dateAsString)!

とできます!

 

ぜひ、一緒にモバイルで新しいアプリやサービスを作ってみたい人、Swift + Realmに挑戦したい人は、info[ @ ]chotchy.comまで!待ってます!