トップ 最新 追記

Cocoa練習帳

iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど

2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|

2017-12-06 [I/O Kit] USB Device Interfaceを試す

以前、Cocoa LifeというCocoa勉強会の会誌で、ジョイスティックをアプリケーションで扱うお話として、HID Device Interface入門という記事を書いたが、これをSwiftで挑戦してみようと思う。

今回対象となるのは、SmartScroll。左手で扱うトラックボール。知人がドライバを書いていたのだが、最新のOSに対応していないので、挑戦することにした。

SmartScroll

以前の開発環境には、USB Proberというアプリが存在したが、今の開発環境ではなくなった。そこで、当時から存在していたIORegistryExplorerを使うことにする。これは、別途、Additional Toolsとしてダウンロードしてインストールするものだ。

以下が、その結果。

IORegistryExplorer

赤い枠で囲まれた値が、機器の特定に必要となる情報だ。

今回、デモ用ということでコマンドラインツールとしてアプリを製作することにしたのだが、戸惑ったのは、main()関数をSwiftではどう書くか?結論は、main()関数は書かないということのようだ。

import Foundation
import CoreFoundation
import IOKit
import IOKit.usb.IOUSBLib
 
exit(0)

main()関数の戻り値は、exit()関数の引数で渡すということになる。

IORegistryExplorerで取得した値のVendor IDとProduct IDを使って機器を特定することにするのだが、それを定数として定義する。

let kOurVendorID = 0x56a    /* Vendor ID of the USB device */
let kOurProductID = 0x50    /* Product ID of device */

機器を探す条件の辞書を用意する。

var masterPort: mach_port_t = 0
let usbVendor: Int32 = Int32(kOurVendorID)
let usbProduct: Int32 = Int32(kOurProductID)
 
let kr: kern_return_t = IOMasterPort(mach_port_t(MACH_PORT_NULL), &masterPort)
 
var matchingDict = IOServiceMatching(kIOUSBDeviceClassName) as? [String: Any]
matchingDict![kUSBVendorID] = usbVendor
matchingDict![kUSBProductID] = usbProduct

検索条件を設定して、RunLoopで待つことにする。

private var gNotifyPort: IONotificationPortRef? = nil
private var gAddedIter: io_iterator_t = 0
 
gNotifyPort = IONotificationPortCreate(masterPort)
let runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort).takeUnretainedValue()
 
private var gRunLoop: CFRunLoop? = CFRunLoopGetCurrent()
CFRunLoopAddSource(gRunLoop, runLoopSource, CFRunLoopMode.defaultMode)
 
let _ = IOServiceAddMatchingNotification(gNotifyPort, kIOFirstMatchNotification, matchingDict! as CFDictionary, DeviceAdded, nil, &gAddedIter)
 
DeviceAdded(nil, gAddedIter)
 
CFRunLoopRun()

機器を見つけると呼ばれる関数の定義は以下の感じだ。

func DeviceAdded(_ refCon: UnsafeMutableRawPointer?, _ iterator: io_iterator_t) {
    print(#function)
}

これで動作確認してみたのだが、うまく動かない。申し訳ないが今回は時間切ということで続きは勉強会で!

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-07 [bukuroswift] BUKURO.swift 2017-12

017年12月6日(水)の夜間、東京都豊島区の池袋コワーキングスペース OpenOffice FORESTにおいて、「BUKURO.swift 2017-12」を開催致した。

発表は、以下の通り。

ぼっち開発について
今回からMAKERS関連の発表をしていこうとなったのだが、それに賛同して用意したのが『ぼっち開発について』。
紹介しているのは日誌を書くこと。自分はこういう話を聞くと、直ぐに日誌をどう実装するかと脱線してしまうが、そんなことにならないように、来年こそは頑張らないと!という気持ちになった。
Swiftリファレンスを作っています
Swiftは仕様が複雑だ!書籍の説明は申し訳ないが冗長だ!だったら自分でリファレンスを作ればいい!githubで公開すれば、執筆手伝ってくれるかも!
ということで、始めた。 Swift_in_a_Nutshell
Swiftで線形代数
自分の欠点は分かっている。目的があったのに、その手前で興味が赴くままに進んでしまう。神経回路網の数理をやりたいのに、その基礎となる線形代数から始めてしまう。BLASを使った例だ。
TensorFlowについて
GoogleのMachine Learning Developer Meetupに参加してきた。ただ、TensorFlowそのものより、登壇された伝説のJeff Dean氏の話題で盛り上がってしまった。
彼のキーボードはキーが二つしかないそうだ。0と1! gccの最適化オプション-o4を指定すると、彼にソースが送られ、最適化されて返ってくる!
BUKURO.makers
ものづくり関連として、昔販売されていた左手で操作するSmartScrollという機器をSwiftでI/O KitのUSB Device Interface使って利用する挑戦が発表された!
三次元グラフフィックスの相談
参加者からの相談で三次元グラフィックスについて、議論が白熱した。これについては、機械をみて、取り上げたいと考えている。
Xcodeのバージョンをあげたら単体テストで警告が
これも参加者からの相談。皆で解決策を探った。

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-08 [自動制御] プログラマと自動制御

卒業研究のタイトルは『拘束のある力学系の制御に関する研究(先端が限定された面上を動くマニピュレータの運動制御の基礎研究)』だったビッツの村上です。全く制御から離れたmacOS/iOSのアプリケーション・プログラマをやっているのだが、今回は、思い切った制御工学に挑戦する。

学生時代から言われたいたのは、制御工学は使われていない。確かにそのようだ。

最初の仕事は人工心肺装置の企画なのだが、病院研修で心臓外科のオペの手伝いをやっていた。心臓手術は心臓に戻ってきた血液を人工心肺装置が取り込み、それを心臓の出口に返して心臓に血液が流れない状態にして行うのだが、循環している血液を冷やすと心臓は止まり、オペ終了時に血液を温めると心臓は動き出す。この血液の温度の調整を水槽で行うのだが、これは、指定された温度になるとヒータが止まり、下がるとヒータがつく。そう、自動制御をやっている方はお分かりだと思うが発信する。これはそういうものだということで特に改善されることはなく、自動制御の出番はなかった。

その後に選んだ職業がプログラマ。またまた、書店で手に取った『神経回路網の数理』に興味を持ち、電子計算機の世界に飛び込んだ。

あるギネスにも載っている巨大なゲートウェイシステムの構築に関係した際のことだ。自分は方式Gというアーキテクトを扱うチームに配属されたのだが、そこで行なっていた流量制御はこんな感じだ。予想される処理コストを決定し、それで性能測定を行い。その値で流量を規制する。そう、ここでも自動制御は活用されていない。予想が外れると性能限界を超えてしまった。内心フィードシステムだ!と思っていたのだが、学生時代に習ったことをプログラミングで、どう表現すればいいのか分からない。

そんな自分にとって、これはと思ったのが、オライリー社の『エンジニアのためのフィードバック制御入門』だ。まえがきに、しっかりと書かれている。自動制御を行うためには電子計算機の力が必要だ。でも、プログラミングの現場では、自動制御が疎遠となっている。この書籍は、この問題に対しての著者からの回答で、ソフトウエアの世界の出来事を自動制御の対象として適用する例が豊富となっている。

また、使っているのもPythonとgnuplot。学生時代、大型計算機でFortranでリカッチ方程式を解いていたのは何だったのか?手元のMacBook Proで演算できちゃいます!

ちなみに、ひねくれ者の自分は、書籍の通り動かすのではなく、SwiftとAccelerate Frameworkを使って、挑戦している。

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-09 [tvOS]tvOS再入門

以前、tvOSアプリケーション開発を試したことがあるが、あれから時間が経過したのと、tvOSのAdvent Calendarに投稿する機会に恵まれたということで、再挑戦することにした。幸い、他の投稿記事には入門的なものはないようなので、ちょうどいいと思っている。

tvOSのアプリケーションには二種類ある。従来型とクライアント-サーバ型だ。

後者はメディアのストリーミングを行うアプリケーションで従来のWeb技術を活用するものだ。マークアップ言語のTVMLでインターフェイスを実装し、JavaScriptで挙動を記述する。TVMLKitフレームワークはネイティブコードとの橋渡しをするものだ。

この投稿では、前者の従来型を取り上げる。Swiftで実装するアプリケーションだ。

サンプル・アプリケーションを用意したので、これを使って説明する。独自のコンテナViewControllerを使って画面を切り替えている。

コンテナ

ContainerViewControllerは子となるViewControllerをインスタンス変数で保持する。

class ContainerViewController: UIViewController {
    var titleViewController: TitleViewController?
    var gameViewController: GameViewController?

Storyboardから子となるViewControllerを取得し、子ViewControllerに親となるコンテナViewController を設定する。

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let mainStoryboard = UIStoryboard.init(name: "Main", bundle: nil)
        titleViewController = mainStoryboard.instantiateViewController(withIdentifier: "TitleViewController") as? TitleViewController
        titleViewController!.containerViewController = self
        gameViewController = mainStoryboard.instantiateViewController(withIdentifier: "GameViewController") as? GameViewController
        gameViewController!.containerViewController = self

コンテナビューコントローラに子ビューコントローラを登録。
        self.addChildViewController(titleViewController!)
        self.addChildViewController(gameViewController!)
        titleViewController!.didMove(toParentViewController: self)
        gameViewController!.didMove(toParentViewController: self)

最初に表示するViewControllerを設定する。

        selectedViewController = titleViewController
        self.view.addSubview(selectedViewController!.view)
タイトル

タイトル画面でStartボタンが選択されるとコンテナViewControllerの画面遷移メソッドを呼ぶ。

class TitleViewController: UIViewController {
    var containerViewController: ContainerViewController?
    
    :
    
    @IBAction func startButtonTapped(_: AnyObject) {
        containerViewController!.toGameViewController()
    }
}

コンテナViewControllerのコードは以下のとおり。

    func toGameViewController() {
        transition(from: titleViewController!, to: gameViewController!, duration: 1.0, options: .transitionCrossDissolve, animations: nil, completion: { (finished: Bool) -> Void in self.selectedViewController = self.gameViewController })
    }

遷移したゲームViewControllerはSpriteKitを使ってゲーム・シーンを表示している。

class GameViewController: UIViewController {
    var containerViewController: ContainerViewController?
    var game: Game?
override func viewDidLoad() { print(NSStringFromClass(type(of: self)), #function) super.viewDidLoad() game = Game() if let aGame = game { let scene = aGame.scene scene!.scaleMode = .aspectFill let skView = view as! SKView skView.presentScene(scene) skView.ignoresSiblingOrder = true skView.showsFPS = true skView.showsNodeCount = true } }

tvOSはiOSから派生しているということで、iOSと同様に記述できている。

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/tvos/Pokopen - GitHub

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-10 [マサカリ]やはりお前らの継承は間違っている。

ほぼ同じコードが複数の箇所に存在している場合、それを共通化すること自体は推奨されることだと思う。ただ、共通化の手法としてクラスの継承を使い、基底クラスに共通コードを実装する場合は手を動かす前に考えた方がいいと思う。オブジェクト指向という名前からも関連があるものがクラス化されるものだと思うが、共通化目的の場合、関連がないものも継承関係に含めてしまっていないかという事を。

これから失敗の事例を説明していく。

共通コードを〜Baseという名前で規定クラスで実装する。

class MyBase {
    func aaa() {
        bbb();
    }
    
    func bbb() {
    }
}
 
class Xxx : MyBase {
    override bbb() {
    }
}
 
class Yyy : MyBase {
    override bbb() {
    }
}

派生クラスの異なる部分をbbb()メソッドで実装する。この瞬間はうまくいっているように見える。

派生クラスXxxとYyyは関係が薄い場合、異なる方向で仕様が変わったとする。そして、上記のコードで都合が悪くなったとする。そんな時、以下のような修正をしてしまわないか?

class MyBase {
    func aaa() {
        if n == abc {
            bbb(2);
        }
        else {
            bbb(1);
        }
    }
    
    func bbb(kind: Int) {
    }
}
 
class Xxx : MyBase {
    override bbb(kind: Int) {
    }
}
 
class Yyy : MyBase {
    override bbb(kind: Int) {
    }
}

基底クラスに派生クラスを考慮したコードが入ってしまう。

この問題を解消するために、基底クラスと派生クラスの間に中間の基底クラスを作成してしまう。そしてくクラスの数の大爆発と派生クラスのコードを読んでも意味が分からない。

この問題の回避は難しい。指摘しても、コードは共通化するものだ。逆にこの手の事をやってしまう人は、積極的にコード構成に意見する場合があるし。

こんな事を投稿してしまった場、マサカリがブーメランとなって自分のところに飛んでこない事を祈っている。

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-11 [swift] 2017年 AKIBA.swift忘年回

本日(12/11)に開催された、2017年 AKIBA.swift忘年回 に参加したので、その内容を報告する。

今回は、忘年回?ということで、設けたテーマに沿った発表が行われた。

  • 続・できるだけUI系のライブラリを用いないアニメーションを盛り込んだサンプル実装の補足
  • Poly API for ARKit
  • iPhone X対応~Safe Areaの外側~
  • AKIBA枠:プログラム出来ますか?
  • AKIBA枠:ヌルオタ的今期アニメ BEST3
  • 番外編

自分も1995年から続く団体や2003年から続く勉強会に参加しているのだが、発表内容のレベルの高さや面白さには驚かされている。
おそらく、今、Swift界隈はとても面白い状況だと思う。メインストリームであることによる高いクオリテイと、近年肩身が狭かったクライアント・サイドのプログラミングをモダン化したSwiftによって趣味性が強い面もあり、これらの相乗効果によって、この状況が生まれたのであろうか?

この幸せな状況をただ単に受け取るだけでなく、より充実していくよう、協力できればと思っている。

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-12 [cocoa] NSWorkspaceとは?

先日のAKIBA.swiftで、macOSでURLを開くAPIが以下なのだが、NSWorkspaceって何?となったので調べてみた。

let urlString = "http://www.bitz.co.jp/"
if let url = URL(string: urlString) {
    NSWorkspace.shared().open(url)
}

NSWorkspaceのWorkspaceは、macOSのFinderに相当する、NeXTSTEPのWorkspace Managerからきている。

Workspace Manager

macOSでは"workspace"サービスを提供するのがNSWorkspaceクラスで、Finderの操作の機能をて提供している。

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-13 [マサカリ]やはりお前らの助っ人は間違っている。

Swift、それはプログラマに残された最後の開拓地である。そこにはプログラマの想像を絶する新しい文法、新しいフレームワークが待ち受けているに違いない。
これはプログラマ最初の試みとしてn年間の調査プログラミングに飛び立った、宇宙船Xcode号の驚異に満ちた物語である。

プログラマ35年定年説というのがあるが、それを超えて働いていると、様々な炎上プロジェクトに遭遇する。そこで知った興味深い事象について報告する。

プロジェクトが炎上すると、人を投入してなんとかするというのは常套手段である。そうやって間に合わない部分、足りない部分を助っ人達に作らせるのだが、炎上がなんとか収まると助っ人達は撤退する。その後は、元のチームのメンバーが助っ人達が作ったコードを保守することになる。

面白いのは、元のチームのメンバーに助っ人のコードのことを聞くと、酷い、というより、知らない、という反応を示すことだ。一切関わりたくないことのようだ。助っ人のコードが本当に酷いのかは分からないが、その後、元のチームのメンバーが、俺には関係ない!という態度で扱えば、そこからバグは発生する者だ。このコードはチームのお荷物となる。

興味深いのは、プロジェクトが長期の場合、元のチームのメンバーが殆ど入れ替わることは驚くべきことではない。でも、元も助っ人も関係ない彼らに助っ人コードのことを聞くと、俺には関係ない!という反応を示すことだ。

助っ人を投入する場合、チームの関係に気を使わないと、そのツケは長期にわたって払わないといけなくなるという、良い教訓だと思う。

今回、新たなスタイルに挑戦してみたが、自分にマサカリが飛んでこないことを願っている。

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-14 [macOS][Metal]MetalKit入門

Metalについては素人なため入門記事しか書けないので申し訳ない。

まず、サンプルプログラムの名前について説明する。
MetalといえばHeavy Metal。ヘビメタのギターといえばIbanezのDestroyer。なのでサンプルプログラムの名前はDestroyer。
調べてみたところ、駆逐艦という意味があるそうだ。これには驚いた。

話をサンプルに戻す。まず、MTKViewの派生クラスを用意する。

import Foundation
import MetalKit
 
class DestroyerView: MTKView {
    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
    }
}

StoryboardでCustom Viewを貼り付け、それのクラスを先ほど作成したクラスに変更する。

Storyboard

これで、DestroyerViewで描画されるようになった。次は、Metal関連のコードを記述していく。

頂点と色の情報の構造体を定義する。

struct Vertex {
    var position: vector_float4
    var color: vector_float4
}

DestroyerViewクラスのメソッドdraw()に描画コードを記述子ていく。

デバイスを作成して設定する。

        self.device = MTLCreateSystemDefaultDevice()

三角形の頂点と色情報を用意

        /* 三角形の頂点と色情報を用意 */
        let vertexData = [Vertex(position: [-0.8, -0.8, 0.0, 1.0], color: [1.0, 0.0, 0.0, 1.0]),
                          Vertex(position: [ 0.8, -0.8, 0.0, 1.0], color: [0.0, 1.0, 0.0, 1.0]),
                          Vertex(position: [ 0.0,  0.8, 0.0, 1.0], color: [0.0, 0.0, 1.0, 1.0]),]
        let vertexBuffer = device?.makeBuffer(bytes: vertexData,
                                              length: MemoryLayout.size(ofValue: vertexData[0]) * vertexData.count,
                                              options: [])

シェーダ・ライブラリを取得する。

        guard let library = device?.makeDefaultLibrary() else {
            return
        }

シェーダーを設定する。

        let vertexFunction = library.makeFunction(name: "vertex_func")
        let fragmentFunction = library.makeFunction(name: "fragment_func")
        let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
        renderPipelineDescriptor.vertexFunction = vertexFunction
        renderPipelineDescriptor.fragmentFunction = fragmentFunction

RGBA 各8bit形式のピクセルを設定する。

renderPipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm

パイプラインステートメントを作成する。

            let renderPipelineState = try device?.makeRenderPipelineState(descriptor: renderPipelineDescriptor)

レンダーパス記述子

            guard let renderPassDescriptor = self.currentRenderPassDescriptor, let drawable = self.currentDrawable else {
                return
            }

クリアする色を設定

            renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.8, 0.7, 0.1, 1.0)

コマンドキューを生成し、コマンドキューからコマンドバッファを生成する

            let commandBuffer = device?.makeCommandQueue()?.makeCommandBuffer()

シェーダへデータを送る

            let renderCommandEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: renderPassDescriptor)
            renderCommandEncoder?.setRenderPipelineState(renderPipelineState!)
            renderCommandEncoder?.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
            renderCommandEncoder?.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3, instanceCount: 1)
            renderCommandEncoder?.endEncoding()

コマンドバッファのコミット

            commandBuffer?.present(drawable)
            commandBuffer?.commit()

シャーダーは、単純に入力値をそのまま返している。

#include >metal_stdlib<
using namespace metal;
 
struct Vertex {
    float4 position [[position]];
    float4 color;
};
 
vertex Vertex vertex_func(constant Vertex *vertices [[buffer(0)]],
                          uint vid [[vertex_id]]) {
    return vertices[vid];
}
 
fragment float4 fragment_func(Vertex vert [[stage_in]]) {
    float3 inColor = float3(vert.color.x, vert.color.y, vert.color.z);
    float4 outColor = float4(inColor.x, inColor.y, inColor.z, 1);
    return outColor;
}

以下の通り、描画される。

Destroyer

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/mac/Destroyer - GitHub

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-15 [macOS] TensorFlowをインストールする

CoreMLではモデルは開発者で用意しないといけない。TensorFlowの利用を考えて、インストールしてみる。

公式サイトの情報によると、macOSでは以下の選択肢がある。

  • virtualenv
  • "native" pip
  • Docker
  • installing from sources
  • Anaconda

お勧めはvirtualenvということなので、素直に従ってみる。

virtualenvをインストールする。

$ sudo easy_install pip
$ pip install --upgrade virtualenv

あれ、二個目のコマンドでエラーになる。パーミッション関連なので、sudoつけてみた。

$ sudo pip install --upgrade virtualenv

成功した。

virtualenv 環境を作成する。標準のPythonは2.7.10なので、以下のコマンドとなる。

$ virtualenv --system-site-packages ~/tensorflow

次はアクティベート。bashなので、以下のコマンドとなる。

$ source ~/tensorflow/bin/activate
(targetDirectory) $

pip 8.1がインストールされていることを確認する。

$ easy_install -U pip

TensorFlowをインストール。

$ pip install --upgrade tensorflow

動作確認する。

$ python
Python 2.7.10 (default, Jul 15 2017, 17:16:57) 
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> # Python
... import tensorflow as tf
>>> hello = tf.constant('Hello, TensorFlow!')
>>> sess = tf.Session()
2017-12-15 22:40:19.713684: I tensorflow/core/platform/cpu_feature_guard.cc:137] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.2 AVX AVX2 FMA
>>> print(sess.run(hello))
Hello, TensorFlow!
>>> 

成功したみたいだ。

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-17 [macOS][iOS]EventKitを試す

仕事効率化のアプリケーションには興味があって、タスク管理アプリを作ろうと考えている。その為の第一歩として、EventKitを試してみる。macOSとiOSの両方を考えているが、サンプルはiOSで。

iOSでは、Info.plistに以下のような定義が必要だ。

	NSCalendarsUsageDescription
	Access the data of the calendar.
	NSRemindersUsageDescription
	Access the data of the reminder.

macOSでは、com.apple.security.personal-information.calendars エンタイトルメントを含めるみたいだが、まだ試していないので紹介に止める。

初期化処理としてイベントストアへの接続というのがあるのだが、ユーザへの認証が必要となる。

let store = EKEventStore()
let status = EKEventStore.authorizationStatus(for: .event)
var isAuth = false
switch status {
case .notDetermined:
    isAuth = false
case .restricted:
    isAuth = false
case .denied:
    isAuth = false
case .authorized:
    isAuth = true
}
if !isAuth {
    store.requestAccess(to: .event, completion: {
        (granted, error) in
        if granted {
        }
        else {
            return
        }
    })
}

デフォルトのカレンダーに対して、過去一年分のイベントを取得する。

let startDate = Date(timeIntervalSinceNow: -365.0 * 24.0 * 60.0 * 60.0)
let endDate = Date()
let defaultCalendar = store.defaultCalendarForNewEvents
let predicate = store.predicateForEvents(withStart: startDate, end: endDate, calendars: [defaultCalendar!])
let events = store.events(matching: predicate)
print(events)

以下の通り、取得できた。

EventKit

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/GettingThingsDone - GitHub

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-18 [macOS][iOS]EventKitを試す(イベントの追加と削除)

前回の続きで、イベントの追加と削除を試す。

イベントを追加する。

let event = EKEvent(eventStore: store)
event.title = "BUKURO.swift 2017-12"
event.startDate = Calendar.current.date(from: DateComponents(year: 2017, month: 12, day: 6, hour: 19, minute: 30, second: 00))
event.endDate = Calendar.current.date(from: DateComponents(year: 2017, month: 12, day: 6, hour: 22, minute: 00, second: 00))
event.calendar = store.defaultCalendarForNewEvents
do {
    try store.save(event, span: .thisEvent)
}
catch let error {
    print(error)
}

イベントを削除する。

do {
    try store.remove(event, span: .thisEvent)
}
catch let error {
    print(error)
}

動作確認が原因で関係者に通知が飛んでしまった。申し訳ない。

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/GettingThingsDone - GitHub

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-19 [macOS][OpenGL]OpenGLに再挑戦

三次元グラフィックスについて触れる機会も増えているということで、OpenGLに再挑戦。

『OpenGLの神髄』のサンプルプログラムを試してみた。

#import <iostream>
#import <sstream>
#import <Foundation/Foundation.h>
#import <OpenGL/gl.h>
#import <OpenGL/glu.h>
#import <GLUT/glut.h>
 
int gWidth = 600;
int gHeight = 500;
const int QUIT_VALUE(99);
GLuint  gListID; /* ディスプレイリストID */
 
void display(void)
{
    /* カラーバッファの初期化 */
    glClear(GL_COLOR_BUFFER_BIT);
    
    /* モデリング変換、z軸の負の方向に幾何形状を4単位移動する。 */
    glLoadIdentity();   /* 単位行列 */
    glTranslatef(0.0f, 0.0f, -4.0f);
    
    /* 幾何形状を描画する。 */
    glCallList(gListID);
    
    /* バッファの入れ替え */
    glutSwapBuffers();
    
    assert(glGetError() == GL_NO_ERROR);
}
 
void resize(int w, int h)
{
    /* ウィンドウ・サイズとOpenGLの座標を対応づける */
    glViewport(0, 0, w, h);
    
    /* 投影行列とアスペクト比を更新する */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(50.0, (GLdouble)w / (GLdouble)h, 1.0, 10.0);
    
    /* 表示ルーチン用にモデルビューモードに設定する */
    glMatrixMode(GL_MODELVIEW);
    
    assert(glGetError() == GL_NO_ERROR);
    
    gWidth = w;
    gHeight = h;
}
 
void keyboard(unsigned char key, int x, int y)
{
    DBGMSG(@"%s", __func__);
}
 
void special(int key, int x, int y)
{
    DBGMSG(@"%s", __func__);
}
 
void mouse(int button, int state, int x, int y)
{
    DBGMSG(@"%s", __func__);
}
 
void motion(int x, int y)
{
    DBGMSG(@"%s", __func__);
}
 
void idle(void)
{
    glutPostRedisplay();
}
 
void main_menu_callback(int value)
{
    if (value == QUIT_VALUE)
        exit(EXIT_SUCCESS);
}
 
void init(void)
{
    DBGMSG(@"%s", __func__);
 
    /* ディザ処理を無効にする */
    glDisable(GL_DITHER);
    
    std::string ver((const char*)glGetString(GL_VERSION));
    assert(! ver.empty());
    std::istringstream verStream(ver);
    
    int major, minor;
    char dummySep;
    verStream >> major >> dummySep >> minor;
    const bool useVertexArrays = ((major >= 1) && (minor >= 1));
    
    const GLfloat data[] = {
        -1.0f, -1.0f, 0.0f,
        1.0f, -1.0f, 0.0f,
        0.0f, 1.0f, 0.0f
    };
    
    if (useVertexArrays) {
        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(3, GL_FLAT, 0, data);
    }
    
    /* ディスプレイリストを作成する。 */
    gListID = glGenLists(1);
    glNewList(gListID, GL_COMPILE);
 
    if (useVertexArrays) {
        glDrawArrays(GL_TRIANGLES, 0, 3);
        //glDisableClientState(GL_VERTEX_ARRAY);
    }
    else {
        glBegin(GL_TRIANGLES);
        glVertex3fv(&data[0]);
        glVertex3fv(&data[3]);
        glVertex3fv(&data[6]);
        glEnd();
    }
    
    glEndList();
    
    assert(glGetError() == GL_NO_ERROR);
    
    /* 描画 */
    glutDisplayFunc(display);
 
    /* リサイズ処理 */
    glutReshapeFunc(resize);
    
    /* キーボード */
    glutKeyboardFunc(keyboard);
    
    /* 特殊キー */
    glutSpecialFunc(special);
    
    /* マウス */
    glutMouseFunc(mouse);
    
    /* ドラッグ */
    glutMotionFunc(motion);
    
    /* バックグランド処理 */
    glutIdleFunc(idle);
    
    /* コンテキスト・メニュー */
    glutCreateMenu(main_menu_callback);
    glutAddMenuEntry("Quit", QUIT_VALUE);
    glutAttachMenu(GLUT_RIGHT_BUTTON);
}
 
int main(int argc, const char * argv[])
{
 
    @autoreleasepool {
        
        glutInit(&argc, (char **)argv);
        
        /* RGBカラーモード ダブルバッファ */
        glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
        
        /* 初期ウィンドウ・サイズ */
        glutInitWindowSize(gWidth, gHeight);
        
        /* 初期ウィンドウ位置 */
        glutInitWindowPosition(500, 100);
        
        /* タイトルバー */
        glutCreateWindow("IRIS GL");
        
        /* 初期化 */
        init();
        
        /* 主ループ(イベント駆動) */
        glutMainLoop();
        
    }
    return 0;
}

glDrawArraysで「Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)」が発生。なぜだ!

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/mac/IRIS - GitHub

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-20 [cocoa]関東swift勉強会2018-01のご案内

_ ■ 関東swift勉強会2018-01 ご案内

iOS/macOSのプログラミングの勉強会です。 Swift/UIKit /AppKit/Objective-Cの技術的な話題を取り上げます。

日時:2019/01/19(金) 19:30-22:00

会場:池袋コワーキングスペース OpenOffice FOREST http://co-forest.com

3階の受付で、swift勉強会又はcocoa勉強会ですとお伝えください。人数によって会場の席が変わる時があります。

集合:現地

座席代:夜間ドロップイン 一人1,000円(各自でお支払いください)

申込:https://cocoa-kanto.connpass.com/event/75006/

発表:募集中です。conpassのアンケート機能で適当に発表内容を書いてもらえば、こちらで以下に追加します。

* 「アウトライプロセッサ進捗会議」(macOS/Swift) 成田

* 「Developer's Tech Lab」(その他)  質問コーナーです。iOS/macOSのプログラミングの質問に答えます。

Cocoa勉強会 関東について:

以下のFacebookグループから、ご参加下さい。

https://www.facebook.com/groups/cocoa.kanto/

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-21 [web]INTER-Mediator勉強会2017-#6

INTER-Mediator勉強会2017-#6 に参加してきたので報告する。

タイムテーブルは以下の通り。

18:00 ~ 開場・交流会
19:00 ~ プレゼン
『クライアントサイドでの書式指定』
『Docker for Macを使ってINTER-Mediatorを試用する』
21:00 ~ クロージング
21:00 ~ 有志による忘年会

今年度は地方での開催が何度か行われた。自分は、残念ながら参加できなかったのだが、来年こそは地方での勉強会に開催できたらと考えている。

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-22 [macOS][OpenGL]OpenGLに再挑戦(其の弐)

前回の続き。

まず、分かったのは、こまめにglGetError()でエラーが発生しているのか確認すること。その結果、glVertexPointer()の呼び出しで、GL_INVALID_ENUM が発生していることが分かった。

そして、OpenGLのバージョンが2.1だったのが、以下の設定で4.1になることが分かった。設定してしまうと、古いAPIが使えなくなるので、一旦、見送るが。

glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_3_2_CORE_PROFILE);

エラーの内容から、バッファオブジェクトを使用しない方法だったが、使用する方法でないと弾かれるのではと考え、試してみることにした。

GLuint  bufferID;
 
void init(void)
{
    assert(glGetError() == GL_NO_ERROR);
    
    /* ディザ処理を無効にする */
    glDisable(GL_DITHER);
    assert(glGetError() == GL_NO_ERROR);
    
    std::string ver((const char*)glGetString(GL_VERSION));
    assert(! ver.empty());
    std::istringstream verStream(ver);
    
    int major, minor;
    char dummySep;
    verStream >> major >> dummySep >> minor;
    const bool useVertexArrays = ((major >= 1) && (minor >= 1));
    NSLog(@"OpenGL Ver. %d.%d", major, minor);
    
    /* 三角形の頂点を定義する */
    const GLfloat data[] = {
        -1.0f, -1.0f, 0.0f,
        1.0f, -1.0f, 0.0f,
        0.0f, 1.0f, 0.0f
    };
    
    if (useVertexArrays) {
        /* バッファIDを取得する */
        glGenBuffers(1, &bufferID);
        
        /* バッファオブジェクトをバインドする */
        glBindBuffer(GL_ARRAY_BUFFER, bufferID);
        
        /* 配列の値をバッファオブジェクトにコピーする */
        glBufferData(GL_ARRAY_BUFFER, 3 * 3 * sizeof(GLfloat), data, GL_STATIC_DRAW);
        
        glEnableClientState(GL_VERTEX_ARRAY);
        assert(glGetError() == GL_NO_ERROR);
        
        glVertexPointer(3, GL_FLAT, 0, bufferObjectPtr(0));
        
        GLenum glErrorCode = glGetError();
        NSLog(@"%s (error code:0x%x)", __func__, glErrorCode);
        assert(glErrorCode == GL_NO_ERROR);
    }
    assert(glGetError() == GL_NO_ERROR);
    
    /* ディスプレイリストを作成する。 */
    gListID = glGenLists(1);
    glNewList(gListID, GL_COMPILE);
    assert(glGetError() == GL_NO_ERROR);
    
    if (useVertexArrays) {
        glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, &gListID);
    }
    else {
        glBegin(GL_TRIANGLES);
        glVertex3fv(&data[0]);
        glVertex3fv(&data[3]);
        glVertex3fv(&data[6]);
        glEnd();
    }
    assert(glGetError() == GL_NO_ERROR);
    
    glEndList();
    
    //NSLog(@"%s (error code:0x%x)", __func__, glGetError());
    assert(glGetError() == GL_NO_ERROR);
    
    /* 描画 */
    glutDisplayFunc(display);
    
    /* リサイズ処理 */
    glutReshapeFunc(resize);
    
    /* キーボード */
    glutKeyboardFunc(keyboard);
    
    /* 特殊キー */
    glutSpecialFunc(special);
    
    /* マウス */
    glutMouseFunc(mouse);
    
    /* ドラッグ */
    glutMotionFunc(motion);
    
    /* バックグランド処理 */
    glutIdleFunc(idle);
    
    /* コンテキスト・メニュー */
    glutCreateMenu(main_menu_callback);
    glutAddMenuEntry("Quit", QUIT_VALUE);
    glutAttachMenu(GLUT_RIGHT_BUTTON);
}

ダメだ。やはり、glVertexPointer() を呼んだ後にエラーだ。

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/mac/IRIS - GitHub

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-24 [macOS][iOS]iCloud Driveに文書を格納する

前回のおさらいから。

ADC文書によると、iCloudストレージを利用する方法は以下となる。

  • キー値ストレージ
  • iCloudドキュメントストレージ
  • Core Dataストレージ
  • CloudKitストレージ

前回、iCloudドキュメントストレージに読み書きしたが、macOSだと ~/Library/Mobile Documents/iCloud~バンドルID/ 配下だった。

iCloud Drive配下でないのだが、どうやればiCloud Driveに読み書きできるのか?調べてみた。

CapabilitiesでiCloudを有効にし、Info.plist に NSUbiquitousContainersキー を追加すればいいようだ。

<key>NSUbiquitousContainers</key>
    <dict>
        <key>iCloud.com.example.MyApp</key>
        <dict>
            <key>NSUbiquitousContainerIsDocumentScopePublic</key>
            <true/>
            <key>NSUbiquitousContainerSupportedFolderLevels</key>
            <string>Any</string>
            <key>NSUbiquitousContainerName</key>
            <string>アプリ名など</string>
        </dict>
    </dict>

NSUbiquitousContainersキー には、コンテナ識別子を設定する。

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/KeepADiary - GitHub

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

2017-12-25 [BUKURO.swift]振り返りと目標

二年前に勉強会の運営方法が変わり一時は活動が停止しそうな状況となりましたが、皆さんのご協力のもと何とか継続してきました。最近は開催頻度を下げ発表の質を高め、他勉強会にも積極的に参加し、内容を見直しして結果なのか、新たな参加者も増えてきました。

来年も、勉強会の活動を通じてプログラマーが楽しくプログラミングできるよう目標を掲げて活動していきたいと思いますので、宜しくお願いします!

  • 勉強会に参加された方全員が学び得る場を提供!
  • 勉強会に参加できない方も学び得るよう、勉強会の成果をアウトプットする!
  • プログラマの拠り所となる!

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Qiita

トップ 最新 追記