フリーランスは見つけてもらうのが全てのはじまり
関西・大阪で活動しているフリーランスアプリ開発者の
ひとのみち(@hitonomichi)です。
嬉しいことがあったので記しておきたいと思います。
検索ワード「フリーランス アプリ 大阪」で1位
記事投稿日現在、Google検索で「フリーランス アプリ 大阪」で1〜4位が私の記事になっています。
これはついにGoogleが私を認めたといっても過言ではありません。
いえ、それは完全に過言ですすみませんすみません。
有名な経済学者のロドミッチ*1が
3つのカテゴリーを掛けあわせてトップになれ
と言ってたのに感銘を受けてからそうなればいいなーと思っていましたが、ひとつ成果が出ました。
私は基本的に開発者なのでプッシュ型営業が出来ません。
プル型営業頼みです。
なので、検索流入はプル型営業のチャンネルとして大切なものだと思っています。
有名な経済学者のロドミッチ*2が
見つけてもらう、存在を知ってもらうのが全てのはじまりだ
と言っていたのを身をもって感じています。
キーワードを関西に変えると3位(記事投稿日現在)になってしまうので、まだまだ小さい範囲ではありますが、ひとつ名前が出やすくなったことが喜ばしい件でした。
結果につながらないと意味が無い
有名な経済学者のロドミッチ*3が提唱するフリーランス営業三原則、
- こんな店あるんだ。
- うまそう。食べてみたい。一回食べてみよ。
- うまかった。また来よう。
このサイクルを大事にしていきたいですね。
まあ検索で特定のワードで上位だーやったーと言ってても意味がないので、
- 今のクライアントから信頼を得る。
- 依頼したくなるような実績を積む。
といった目前の道の一歩をまずは大事にしていこうと思います。
10kmたまごも一歩から。
お仕事のお問い合わせはこちらからお願い致します。
ROADTO Webページ
実績はこちら
フリーランスアプリ開発者が2016年4月〜6月に通った道
関西・大阪在住、フリーランスアプリ開発者の私@hitonomichiの2016年4月〜6月の活動まとめです。
前回の活動まとめから3ヶ月経ったので自分自身での振り返りがてら、
3ヶ月間の総括をします。
長いので夏のバーゲンを調査し終わってから見てください。
お仕事のお問い合わせはこちらから。
ROADTO Webページ
トピック
2016年4月〜6月に通った道
- 大阪ミナミのクーポンアプリ
- BLE連携アプリ
- スタッフコミュニケーションアプリ
- 研究機関の診断アプリ
- シェアオフィスに入居
それではいってみましょう(σ・∀・)σ
順不同です。
大阪ミナミのクーポンアプリ
AppStore向けアプリの受託案件。
大阪ミナミ地域の店舗集客アプリです。
外国人向けのクーポンアプリですね。
「i-Pon」
iBeaconを搭載しています。
アプリを起動すると、近くにお店があれば自動的に知らせてくれます。
iBeacon(アイビーコン)とは発信機みたいなもので、近くにいるとお知らせしてくれます。
ブラッシュアップしていく予定です。
サーバ側プログラムとデザインはご提供頂いて、iOSアプリ開発を担当しました。
アプリだけ作って欲しいんだよねーという場合にはこういう形でも参画します!笑
BLE連携アプリ
AppStore向けアプリの受託案件。
Appleの審査もパスしていますがまだ世に出てはいません。
BLEガジェット同士を連動したり、
BLEガジェットとスマホならではの機能を連動したりします。
前述のiBeaconもそうですがBLEを扱うことが増えてきて嬉しい!
センサーやWebの情報も動作条件として組み込んでいく予定です。
用語からさっぱりだったBLEの知識もだいぶ付いたはずです。
いまだにわけわからない事もたくさんありますが・・。
スタッフコミュニケーションサービス
人材派遣業向けのスタッフ管理サービスです。
Webとスマホでスマートにコミュニケーションが出来ることを目指します。
現在開発中なのですが、iOSの開発と、その他開発に関わるディレクションをしています。
そんな言葉あるのか知らないけど、プレーイングディレクターとでも言いましょうか。
チームも、必要なポジションにプロがいるので心強いです。
その業界の中の人 → ノウハウ、経験、コネクション
開発者 → 具現化、価値化
必要な条件がいい感じに揃っているのでいい方向に行くんじゃないかと考えています。
まずは良い物を作るところから。
研究機関の診断アプリ
ブラッシュアップ案件。
ツール系のデータを集めるアプリなので小さな変更でも気を使います。
ドキドキ。
研究機関の診断アプリは継続してお仕事頂戴してる企業様からの案件です。
リピート頂きいつもありがとうございます!
シェアオフィスに入居
6月からシェアオフィスに入りました。
ベンチャーがたくさんいて頻繁に交流会やセミナーがあり、VCが来てお金の話をしていたり(私のところへではないよ)、刺激的な毎日です。
家で一人で作業して、奥さん子供とほのぼのしてた時とは全然違う(笑)
外に出ると毎日新しい事を知る。
— ひとのみち (@hitonomichi) June 21, 2016
その度に気持ちが波打つ。
脳が動く。
入居して一ヶ月なのでだいぶリズムも出てき、浮ついた感じもなくなってきたので、
最近は地に足を付けてもくもくと作業しています。
総括
前回の総括でのプランからどうだったか。
受託案件中心にしたいと言っていたけど?
完全に受託のみに。
そして受託だけでなくサービス開発に発展してきました。
がんばります。
iPhone+何かをしたいと言っていたけど?
BLEガジェットの案件もやってAppleの審査も通ったのでまずまず。
実績としてもBLE案件やりました!といえる! はず。
一人からの脱却と変化と言っていたけど?
Webのエンジニアと協業するようになりました。
会社じゃないし一人からの脱却までいかないけどまず一歩。
得意分野を活かして補完しあってお互い幸せというのを大事にしたいです。
2016年7月〜9月への道しるべ
これからについて。
サービスの立ち上げ
がっつりこれだけという訳にはいかないですが、メインの仕事として続きます。
まずはiOSアプリを良い物に作り上げます。
チームでやるのでパッション持ってやれそうです。
引き続き受託
半分は受託をする見込みです。
ありがたいことにいくつかお話は頂いています。
リピートして頂けるよう価値のあるものにしたいです。
あとiPhone+何かの仕事も。
おわりに
外に出るようになって、通勤とか面倒なところもありますが、
ちょっとした運動と外部刺激になって良いところもあります。
体調も朝5:30出発が習慣になっててすこぶる良いです。
仕事は開発するのみではない立場になったので、勉強しながらかつ自信持って進めていこうと思います。
WWDCがあったり日本の首都の偉い人が辞めますぞえって言ったりイギリスEU離脱があったり、
世間は騒がしいですが、しっかり地に足を付けて頑張ろうと思います。
皆様どうぞ今夏もよろしくお願いいたします。
お仕事のお問い合わせはこちらからお願いします。
ROADTO Webページ
過去の道
- 前回の活動まとめ
- 過去の実績まとめ hitonomichi.hatenablog.com
出勤。
— ひとのみち (@hitonomichi) June 3, 2016
本日は実装とディレクションを。
最後にオリーブオイルを。
WWDC2016セッションメモ「A Peek at 3D Touch」
WWDC2016セッションを見たメモです。
文中の画像やコードは公開情報から引用しています。
正確で詳しい内容をお求めの方は一次情報の確認をおすすめします。
A Peek at 3D Touch
A Peek at 3D Touch - WWDC 2016 - Videos - Apple Developer
ぐっ ぶっ ぴ のやつ。
こんなやつ。
こんなやつとか。
UIPreviewInteraction
新しいやつ。
アプリ内の3D Touchに関する技術。
3D Touchが扱いやすくなりましたよ。
UIPreviewInteractionDelegate
プロトコルに準拠します。
extension ChatDetailViewController :UIPreviewInteractionDelegate
UIPreviewInteraction
UIPreviewInteractionを生成します。
override func viewDidLoad() { super.viewDidLoad() replyPreviewInteraction = UIPreviewInteraction(view: view) replyPreviewInteraction.delegate = self }
状態が二段階になってる。
開始〜プレビューの状態と、プレビュー〜コミットの状態。
ぐっと押して、プレビュー表示の状態。
ぐぐっと押して、コミット完了した状態。
デリゲートメソッド
開始されたときのデリゲートメソッド。
previewInteractionShouldBegin()
開始〜プレビューまでの状態の時に、
押す強さが更新されたら呼ばれるデリゲートメソッド。
func previewInteraction(_ previewInteraction: UIPreviewInteraction, didUpdatePreviewTransition transitionProgress: CGFloat, ended: Bool) { //ビューの更新処理 updateForPreview(progress: transitionProgress) if ended { //プレビューの完了処理 completePreview() } }
プレビューがキャンセルされたら呼ばれるデリゲートメソッド。
指を離した時ですね。
func previewInteractionDidCancel(_ previewInteraction: UIPreviewInteraction) { //キャンセル処理 UIView.animate(withDuration: 0.4) { self.updateForPreview(progress: 0) self.resetToInitialAppearance() } }
プレビュー〜コミットまでの時に、
押す強さが更新されたら呼ばれるデリゲートメソッド。
func previewInteraction(_ previewInteraction: UIPreviewInteraction, didUpdateCommitTransition transitionProgress: CGFloat, ended: Bool) { //ビューの更新処理 updateForCommit(progress: transitionProgress) if ended { //コミットの完了処理 completeCommit() } }
感想
この操作ができることをどうやってユーザーに知らせるかが課題ではある。
扱いやすくなったのではりきって3D Touchするぞー!
WWDC2016セッションメモシリーズはこちら
WWDC2016 カテゴリーの記事一覧 - ROADTO みちログ
WWDCビデオ「A Peek at 3D Touch」拝聴。
— ひとのみち (@hitonomichi) 2016年6月26日
操作の速攻性はあるから使いたい。どうやってこの機能があるか知らせば良いのか。チュートリアルか。
WWDC2016セッションメモ「Advanced Notifications」
WWDC2016セッションを見たメモです。
文中の画像やコードは公開情報から引用しています。
正確で詳しい内容をお求めの方は一次情報の確認をおすすめします。
Advanced Notifications
Advanced Notifications - WWDC 2016 - Videos - Apple Developer
Media Attachments
{ aps: { alert: { ... }, mutable-content: 1 } my-attachment: "https://example.com/photo.jpg" }

// Adding an attachment to a user notification public class NotificationService: UNNotificationServiceExtension { override public func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: (UNNotificationContent) -> Void) { let fileURL = // ... let attachment = UNNotificationAttachment(identifier: "image", url: fileURL, options: nil) let content = request.content.mutableCopy as! UNMutableNotificationContent content.attachments = [ attachment ] contentHandler(content) } }
Media Attachments は、
Local notifications も Remote notifications も対応。
対応してるのは画像、音声、ビデオ。
時間やサイズに制限はあるよ。
Custom User Interface
通知内容を拡張できる。
ビューをカスタムできる。
通知アクションに応答する。
Notification Content Extension
「Notification Content」を選択。

UNNotificationContentExtension を継承したUIViewControllerのサブクラスができる。
カスタムビュー
class NotificationViewController: UIViewController, UNNotificationContentExtension { @IBOutlet var eventTitle: UILabel! @IBOutlet var eventDate: UILabel! @IBOutlet var eventLocation: UILabel! @IBOutlet var eventMessage: UILabel! func didReceive(_ notification: UNNotification) { let content = notification.request.content eventTitle.text = content.title eventDate.text = content.subtitle eventMessage.text = content.body if let location = content.userInfo["location"] as? String { eventLocation.text = location } } }
eventMessageの箇所

eventMessageの箇所

plistにいろいろ設定するらしい。


こうなる。
The Bash〜のところがUNNotificationExtensionInitialContentSizeRatioで措定されている。
Default content が非表示になってる。
メディアの追加
設定する。
// Notification Content Extension Attachments class NotificationViewController: UIViewController, UNNotificationContentExtension { @IBOutlet var eventImage: UIImageView! func didReceive(_ notification: UNNotification) { let content = notification.request.content if let attachment = content.attachments.first { if attachment.url.startAccessingSecurityScopedResource() { eventImage.image = UIImage(contentsOfFile: attachment.url.path!) attachment.url.stopAccessingSecurityScopedResource() } } } }
こうなる。

アクションとレスポンス
// Intercepting notification action response class NotificationViewController: UIViewController, UNNotificationContentExtension { func didReceive(_ response: UNNotificationResponse, completionHandler done: (UNNotificationContentExtensionResponseOption) -> Void) { server.postEventResponse(response.actionIdentifier) { if response.actionIdentifier == "accept" { eventResponse.text = "Going!" eventResponse.textColor = UIColor.green() } else if response.actionIdentifier == "decline" { eventResponse.text = "Not going :(" eventResponse.textColor = UIColor.red() } // done(.dismiss) done(.dismissAndForwardAction) } } } }
こうなる。

テキスト入力
UNNotificationCategoryをつくる。
// Text Input Action private func makeEventExtensionCategory() -> UNNotificationCategory { let commentAction = UNTextInputNotificationAction( identifier: "comment", title: "Comment", options: [], textInputButtonTitle: "Send", textInputPlaceholder: "Type here...") return UNNotificationCategory(identifier: "event-invite", actions: [ acceptAction, declineAction, commentAction ], }
こうなる。

// Text input action response class NotificationViewController: UIViewController, UNNotificationContentExtension { func didReceive(_ response: UNNotificationResponse, completionHandler done: (UNNotificationContentExtensionResponseOption) -> Void) { if let textResponse = response as? UNTextInputNotificationResponse { server.send(textResponse.userText) { done(.dismiss) } } } }
アクセサリーのカスタム(?)
// Custom input accessory view class NotificationViewController: UIViewController, UNNotificationContentExtension { override func canBecomeFirstResponder() -> Bool { return true } override var inputAccessoryView: UIView { get { return inputView } } func didReceive(_ response: UNNotificationResponse, completionHandler done: (UNNotificationContentExtensionResponseOption) -> Void) { } }
感想
出来ることが増えてここだけで機能完結できることもありそう。 がんばってリッチな通知するぞー!
WWDC2016セッションメモシリーズはこちら
WWDC2016 カテゴリーの記事一覧 - ROADTO みちログ
「Advanced Notifications」拝聴。通知に画像、音声、ビデオが対応。
— ひとのみち (@hitonomichi) 2016年6月24日
リッチ〜。
WWDC2016セッションメモ「Introduction to Notifications」
WWDC2016セッションを見たメモです。
文中の画像やコードは公開情報から引用しています。
正確で詳しい内容をお求めの方は一次情報の確認をおすすめします。
Introduction to Notifications
Introduction to Notifications - WWDC 2016 - Videos - Apple Developer
User Notifications Framework

watchOS単独でLocalNotificationが使える
Registration
使うのにRegistration が必要。
ローカルもリモートも必要。
UNUserNotificationCenter.current().requestAuthorization([.alert, .sound, .badge]) { (granted, error) in /* ... */}
ユーザ定義を設定する
UNUserNotificationCenter.current().getNotificationSettings { (settings) in /* ... */}
コンテンツ
TitleとSubtitleが追加された。
画像とかメディアも表示出来る。

Local Notification
let content = UNMutableNotificationContent() content.title = "Introduction to Notifications" content.subtitle = "Session 707" content.body = "Woah! These new notifications look amazing! Don’t you agree?" content.badge = 1
Remote Notification
{ "aps" : { "alert" : { "title" : "Introduction to Notifications", "subtitle" : "Session 707", "body" : "Woah! These new notifications look amazing! Don’t you agree?" }, "badge" : 1 }, }
トリガー
Time Interval
//2分後に発火 UNTimeIntervalNotificationTrigger(timeInterval: 120, repeats: false) //リピートするならrepeatsをtrueに。
Calendar
//「明日朝8時に」「毎週月曜日の18時に」とか指定できる。 let dateComponents = DateComponents() // dateComponentsに設定する UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
Location
//「家についたら」「アモーレと思い出の場所に近づいたら」とか指定できる。 let region = CLRegion() // ここでregionに設定する UNLocationNotificationTrigger(region: region, repeats: false)
どうやるのか

// Notification Delivery Summary import UserNotifications //許可 UNUserNotificationCenter.current().requestAuthorization([.alert, .sound, .badge]) { (granted, error) in // ... } //コンテンツ指定 let content = UNMutableNotificationContent() content.title = "Introduction to Notifications" content.body = "Let's talk about notifications!" //トリガー指定 let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false) //リクエスト作成 let requestIdentifier = "sampleRequest" let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger) //UNUserNotificationCenterに追加 UNUserNotificationCenter.current().add(request) { (error) in // ... }
ハンドリング
フォアグラウンドの時アプリ内表示ハンドリング
protocol UNUserNotificationCenterDelegate : NSObjectProtocol func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler handlerBlock: (UNNotificationPresentationOptions) -> Void) { // Roll banner and sound alert handlerBlock([.alert, .sound]) }
Notification Management
たくさんの通知うっとおしいよね。
最新の一件だけに更新できますよ。

保留の通知を消す
保留の通知を消すこともできますよ。
// Pending Notification Removal let gameStartIdentifier = "game1.start.identifier" let gameStartRequest = UNNotificationRequest(identifier: gameStartIdentifier, content: content, trigger: startTrigger) UNUserNotificationCenter.current().add(gameStartRequest) { (error) in // ... } // Game was cancelled UNUserNotificationCenter.current.current() .removePendingNotificationRequests(withIdentifiers: [gameStartIdentifier])
保留の通知を更新
保留の通知を更新することもできますよ。
// Pending Notification Update let gameStartIdentifier = "game1.start.identifier" let gameStartRequest = UNNotificationRequest(identifier: gameStartIdentifier, content: content, trigger: startTrigger) UNUserNotificationCenter.current().add(gameStartRequest) { (error) in // ... } // Game start time was updated let updatedGameStartRequest = UNNotificationRequest(identifier: gameStartIdentifier, content: content, trigger: newStartTrigger) UNUserNotificationCenter.current().add(updatedGameStartRequest) { (error) in // ... }
配信済みの通知を削除
// Delivered Notification Removal let gameScoreIdentifier = "game1.score.identifier" let gameScoreRequest = UNNotificationRequest(identifier: gameScoreIdentifier, content: scoreContent, trigger: trigger) UNUserNotificationCenter.current().add(gameScoreRequest) { (error) in // ... } // Wrong game score was published UNUserNotificationCenter.current() .removeDeliveredNotifications(withIdentifiers: [gameScoreIdentifier])
配信済みの通知を更新
// Delivered Notification Update let gameScoreIdentifier = "game1.score.identifier" let gameScoreRequest = UNNotificationRequest(identifier: gameScoreIdentifier, content: scoreContent, trigger: trigger) UNUserNotificationCenter.current().add(gameScoreRequest) { (error) in // ... } // Score game was updated let updateGameScoreRequest = UNNotificationRequest(identifier: gameScoreIdentifier, content: newScoreContent, trigger: newTrigger) UNUserNotificationCenter.current().add(updateGameScoreRequest) { (error) in // ... }
アクション付き通知
- ボタン付き
- 返信とかのテキスト入力
- iOSとwatchOSで使えます。

let action = UNNotificationAction(identifier:"reply",title:"Reply",options:[]) let category = UNNotificationCategory(identifier: "message", actions: [action], minimalActions: [action], intentIdentifiers: [], options: []) UNUserNotificationCenter.current().setNotificationCategories([category])
非表示アクション
通知をスワイプで消せますよ。
optionsに設定します。
customDismissAction: UNNotificationCategoryOptions let category = UNNotificationCategory(identifier: "message", actions: [action], minimalActions: [action], intentIdentifiers: [], options: [.customDismissAction])
感想
できること増えたね!
WWDC2016セッションメモシリーズはこちら
WWDC2016 カテゴリーの記事一覧 - ROADTO みちログ
「Introduction to Notifications」拝聴。出来ること増えたしツール系は嬉しい。
— ひとのみち (@hitonomichi) 2016年6月23日
ゲームにももちろん使えるけど広告になるとうざいかも。
WWDC2016セッションメモ「AVCapturePhotoOutput—Beyond the Basics」
WWDC2016セッションを見たメモです。
文中の画像やコードは公開情報から引用しています。
正確で詳しい内容をお求めの方は一次情報の確認をおすすめします。
AVCapturePhotoOutput—Beyond the Basics
AVCapturePhotoOutput - Beyond the Basics - WWDC 2016 - Videos - Apple Developer
アクセスの確認が変わった
iOS10からカメラ、音声、写真アクセスの許可時に理由の記載が必須になる。

設定はplistに追加する。
方法はこちら。
plistに直接書くならこう。
<plist version="1.0"> <dict> <key>NSCameraUsageDescription</key> <string>to take photos and video</string> <key>NSMicrophoneUsageDescription</key> <string>to record Live Photos and movies</string> <key>NSPhotoLibraryUsageDescription</key> <string>to save photos and videos</string> </dict> </plist>
感想
カメラはともかく写真使うの結構あるから対応しないと。
WWDC2016セッションメモシリーズはこちら
WWDC2016セッションメモ「What's New in Swift」
WWDC2016セッションを見たメモです。
文中の画像やコードは公開情報から引用しています。
正確で詳しい内容をお求めの方は一次情報の確認をおすすめします。
What's New in Swift
What's New in Swift - WWDC 2016 - Videos - Apple Developer
名前が変わった。
NSが付かなくなりますよ。
APIが変わった。
Swift2.2
↓↓↓
Swift3
パラメータのラベリングが変わった
Swift2.2
↓↓↓
Swift3
ひとつ目のパラメータにラベルが付いてわかりやすくなったね。
なくなるのもあるよ
インクリメントの++がなくなる。
昔からのfor文の書き方ができなくなる。
など。
マイグレーション
Swift2.2→3へ変換してくれる。
感想
張り切って変換するぞー。。
WWDC2016セッションメモシリーズはこちら
WWDC2016 カテゴリーの記事一覧 - ROADTO みちログ
What's New in Swiftから。今日は何本見れるかな。
— ひとのみち (@hitonomichi) 2016年6月15日