PageSourceSearch

https://entaku.dev/_next/static/chunks/42nvkw_87n17_.js

js entaku.dev collected 2026-10-09 22:07:48 UTC 62,085 bytes, 1 lines download raw bytes

1(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,20955,(e,t,i)=>{var r={229:function(e){var t,i,r,n=e.exports={};function a(){throw Error("setTimeout has not been defined")}function o(){throw Error("clearTimeout has not been defined")}try{t="function"==typeof setTimeout?setTimeout:a}catch(e){t=a}try{i="function"==typeof clearTimeout?clearTimeout:o}catch(e){i=o}function s(e){if(t===setTimeout)return setTimeout(e,0);if((t===a||!t)&&setTimeout)return t=setTimeout,setTimeout(e,0);try{return t(e,0)}catch(i){try{return t.call(null,e,0)}catch(i){return t.call(this,e,0)}}}var l=[],c=!1,u=-1;function p(){c&&r&&(c=!1,r.length?l=r.concat(l):u=-1,l.length&&d())}function d(){if(!c){var e=s(p);c=!0;for(var t=l.length;t;){for(r=l,l=[];++u<t;)r&&r[u].run();u=-1,t=l.length}r=null,c=!1,function(e){if(i===clearTimeout)return clearTimeout(e);if((i===o||!i)&&clearTimeout)return i=clearTimeout,clearTimeout(e);try{i(e)}catch(t){try{return i.call(null,e)}catch(t){return i.call(this,e)}}}(e)}}function m(e,t){this.fun=e,this.array=t}function h(){}n.nextTick=function(e){var t=Array(arguments.length-1);if(arguments.length>1)for(var i=1;i<arguments.length;i++)t[i-1]=arguments[i];l.push(new m(e,t)),1!==l.length||c||s(d)},m.prototype.run=function(){this.fun.apply(null,this.array)},n.title="browser",n.browser=!0,n.env={},n.argv=[],n.version="",n.versions={},n.on=h,n.addListener=h,n.once=h,n.off=h,n.removeListener=h,n.removeAllListeners=h,n.emit=h,n.prependListener=h,n.prependOnceListener=h,n.listeners=function(e){return[]},n.binding=function(e){throw Error("process.binding is not supported")},n.cwd=function(){return"/"},n.chdir=function(e){throw Error("process.chdir is not supported")},n.umask=function(){return 0}}},n={};function a(e){var t=n[e];if(void 0!==t)return t.exports;var i=n[e]={exports:{}},o=!0;try{r[e](i,i.exports,a),o=!1}finally{o&&delete n[e]}return i.exports}a.ab="/ROOT/node_modules/next/dist/compiled/process/",t.exports=a(229)},50461,(e,t,i)=>{"use strict";var r,n;t.exports=(null==(r=e.g.process)?void 0:r.env)&&"object"==typeof(null==(n=e.g.process)?void 0:n.env)?e.g.process:e.r(20955)},41705,(e,t,i)=>{"use strict";i._=function(e){return e&&e.__esModule?e:{default:e}}},8481,(e,t,i)=>{"use strict";var r=Symbol.for("react.transitional.element");function n(e,t,i){var n=null;if(void 0!==i&&(n=""+i),void 0!==t.key&&(n=""+t.key),"key"in t)for(var a in i={},t)"key"!==a&&(i[a]=t[a]);else i=t;return{$$typeof:r,type:e,key:n,ref:void 0!==(t=i.ref)?t:null,props:i}}i.Fragment=Symbol.for("react.fragment"),i.jsx=n,i.jsxs=n},91398,(e,t,i)=>{"use strict";t.exports=e.r(8481)},61556,(e,t,i)=>{"use strict";var r=e.i(50461),n=Symbol.for("react.transitional.element"),a=Symbol.for("react.portal"),o=Symbol.for("react.fragment"),s=Symbol.for("react.strict_mode"),l=Symbol.for("react.profiler"),c=Symbol.for("react.consumer"),u=Symbol.for("react.context"),p=Symbol.for("react.forward_ref"),d=Symbol.for("react.suspense"),m=Symbol.for("react.memo"),h=Symbol.for("react.lazy"),f=Symbol.for("react.activity"),g=Symbol.iterator,b={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},v=Object.assign,y={};function w(e,t,i){this.props=e,this.context=t,this.refs=y,this.updater=i||b}function S(){}function k(e,t,i){this.props=e,this.context=t,this.refs=y,this.updater=i||b}w.prototype.isReactComponent={},w.prototype.setState=function(e,t){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")},w.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},S.prototype=w.prototype;var A=k.prototype=new S;A.constructor=k,v(A,w.prototype),A.isPureReactComponent=!0;var C=Array.isArray;function I(){}var x={H:null,A:null,T:null,S:null},j=Object.prototype.hasOwnProperty;function O(e,t,i){var r=i.ref;return{$$typeof:n,type:e,key:t,ref:void 0!==r?r:null,props:i}}function P(e){return"object"==typeof e&&null!==e&&e.$$typeof===n}var T=/\/+/g;function _(e,t){var i,r;return"object"==typeof e&&null!==e&&null!=e.key?(i=""+e.key,r={"=":"=0",":":"=2"},"$"+i.replace(/[=:]/g,function(e){return r[e]})):t.toString(36)}function E(e,t,i){if(null==e)return e;var r=[],o=0;return!function e(t,i,r,o,s){var l,c,u,p=typeof t;("undefined"===p||"boolean"===p)&&(t=null);var d=!1;if(null===t)d=!0;else switch(p){case"bigint":case"string":case"number":d=!0;break;case"object":switch(t.$$typeof){case n:case a:d=!0;break;case h:return e((d=t._init)(t._payload),i,r,o,s)}}if(d)return s=s(t),d=""===o?"."+_(t,0):o,C(s)?(r="",null!=d&&(r=d.replace(T,"$&/")+"/"),e(s,i,r,"",function(e){return e})):null!=s&&(P(s)&&(l=s,c=r+(null==s.key||t&&t.key===s.key?"":(""+s.key).replace(T,"$&/")+"/")+d,s=O(l.type,c,l.props)),i.push(s)),1;d=0;var m=""===o?".":o+":";if(C(t))for(var f=0;f<t.length;f++)p=m+_(o=t[f],f),d+=e(o,i,r,p,s);else if("function"==typeof(f=null===(u=t)||"object"!=typeof u?null:"function"==typeof(u=g&&u[g]||u["@@iterator"])?u:null))for(t=f.call(t),f=0;!(o=t.next()).done;)p=m+_(o=o.value,f++),d+=e(o,i,r,p,s);else if("object"===p){if("function"==typeof t.then)return e(function(e){switch(e.status){case"fulfilled":return e.value;case"rejected":throw e.reason;default:switch("string"==typeof e.status?e.then(I,I):(e.status="pending",e.then(function(t){"pending"===e.status&&(e.status="fulfilled",e.value=t)},function(t){"pending"===e.status&&(e.status="rejected",e.reason=t)})),e.status){case"fulfilled":return e.value;case"rejected":throw e.reason}}throw e}(t),i,r,o,s);throw Error("Objects are not valid as a React child (found: "+("[object Object]"===(i=String(t))?"object with keys {"+Object.keys(t).join(", ")+"}":i)+"). If you meant to render a collection of children, use an array instead.")}return d}(e,r,"","",function(e){return t.call(i,e,o++)}),r}function K(e){if(-1===e._status){var t=e._result;(t=t()).then(function(t){(0===e._status||-1===e._status)&&(e._status=1,e._result=t)},function(t){(0===e._status||-1===e._status)&&(e._status=2,e._result=t)}),-1===e._status&&(e._status=0,e._result=t)}if(1===e._status)return e._result.default;throw e._result}var D="function"==typeof reportError?reportError:function(e){if("object"==typeof window&&"function"==typeof window.ErrorEvent){var t=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:"object"==typeof e&&null!==e&&"string"==typeof e.message?String(e.message):String(e),error:e});if(!window.dispatchEvent(t))return}else if("object"==typeof r.default&&"function"==typeof r.default.emit)return void r.default.emit("uncaughtException",e);console.error(e)};i.Activity=f,i.Children={map:E,forEach:function(e,t,i){E(e,function(){t.apply(this,arguments)},i)},count:function(e){var t=0;return E(e,function(){t++}),t},toArray:function(e){return E(e,function(e){return e})||[]},only:function(e){if(!P(e))throw Error("React.Children.only expected to receive a single React element child.");return e}},i.Component=w,i.Fragment=o,i.Profiler=l,i.PureComponent=k,i.StrictMode=s,i.Suspense=
1d,i.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=x,i.__COMPILER_RUNTIME={__proto__:null,c:function(e){return x.H.useMemoCache(e)}},i.cache=function(e){return function(){return e.apply(null,arguments)}},i.cacheSignal=function(){return null},i.cloneElement=function(e,t,i){if(null==e)throw Error("The argument must be a React element, but you passed "+e+".");var r=v({},e.props),n=e.key;if(null!=t)for(a in void 0!==t.key&&(n=""+t.key),t)j.call(t,a)&&"key"!==a&&"__self"!==a&&"__source"!==a&&("ref"!==a||void 0!==t.ref)&&(r[a]=t[a]);var a=arguments.length-2;if(1===a)r.children=i;else if(1<a){for(var o=Array(a),s=0;s<a;s++)o[s]=arguments[s+2];r.children=o}return O(e.type,n,r)},i.createContext=function(e){return(e={$$typeof:u,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null}).Provider=e,e.Consumer={$$typeof:c,_context:e},e},i.createElement=function(e,t,i){var r,n={},a=null;if(null!=t)for(r in void 0!==t.key&&(a=""+t.key),t)j.call(t,r)&&"key"!==r&&"__self"!==r&&"__source"!==r&&(n[r]=t[r]);var o=arguments.length-2;if(1===o)n.children=i;else if(1<o){for(var s=Array(o),l=0;l<o;l++)s[l]=arguments[l+2];n.children=s}if(e&&e.defaultProps)for(r in o=e.defaultProps)void 0===n[r]&&(n[r]=o[r]);return O(e,a,n)},i.createRef=function(){return{current:null}},i.forwardRef=function(e){return{$$typeof:p,render:e}},i.isValidElement=P,i.lazy=function(e){return{$$typeof:h,_payload:{_status:-1,_result:e},_init:K}},i.memo=function(e,t){return{$$typeof:m,type:e,compare:void 0===t?null:t}},i.startTransition=function(e){var t=x.T,i={};x.T=i;try{var r=e(),n=x.S;null!==n&&n(i,r),"object"==typeof r&&null!==r&&"function"==typeof r.then&&r.then(I,D)}catch(e){D(e)}finally{null!==t&&null!==i.types&&(t.types=i.types),x.T=t}},i.unstable_useCacheRefresh=function(){return x.H.useCacheRefresh()},i.use=function(e){return x.H.use(e)},i.useActionState=function(e,t,i){return x.H.useActionState(e,t,i)},i.useCallback=function(e,t){return x.H.useCallback(e,t)},i.useContext=function(e){return x.H.useContext(e)},i.useDebugValue=function(){},i.useDeferredValue=function(e,t){return x.H.useDeferredValue(e,t)},i.useEffect=function(e,t){return x.H.useEffect(e,t)},i.useEffectEvent=function(e){return x.H.useEffectEvent(e)},i.useId=function(){return x.H.useId()},i.useImperativeHandle=function(e,t,i){return x.H.useImperativeHandle(e,t,i)},i.useInsertionEffect=function(e,t){return x.H.useInsertionEffect(e,t)},i.useLayoutEffect=function(e,t){return x.H.useLayoutEffect(e,t)},i.useMemo=function(e,t){return x.H.useMemo(e,t)},i.useOptimistic=function(e,t){return x.H.useOptimistic(e,t)},i.useReducer=function(e,t,i){return x.H.useReducer(e,t,i)},i.useRef=function(e){return x.H.useRef(e)},i.useState=function(e){return x.H.useState(e)},i.useSyncExternalStore=function(e,t,i){return x.H.useSyncExternalStore(e,t,i)},i.useTransition=function(){return x.H.useTransition()},i.version="19.2.7"},91788,(e,t,i)=>{"use strict";t.exports=e.r(61556)},13584,(e,t,i)=>{"use strict";Object.defineProperty(i,"__esModule",{value:!0}),Object.defineProperty(i,"HeadManagerContext",{enumerable:!0,get:function(){return r}});let r=e.r(41705)._(e.r(91788)).default.createContext({})},94470,(e,t,i)=>{"use strict";Object.defineProperty(i,"__esModule",{value:!0}),Object.defineProperty(i,"warnOnce",{enumerable:!0,get:function(){return r}});let r=e=>{}},52456,(e,t,i)=>{"use strict";function r(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,i=new WeakMap;return(r=function(e){return e?i:t})(e)}i._=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var i=r(t);if(i&&i.has(e))return i.get(e);var n={__proto__:null},a=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var s=a?Object.getOwnPropertyDescriptor(e,o):null;s&&(s.get||s.set)?Object.defineProperty(n,o,s):n[o]=e[o]}return n.default=e,i&&i.set(e,n),n}},94941,(e,t,i)=>{"use strict";Object.defineProperty(i,"__esModule",{value:!0}),Object.defineProperty(i,"default",{enumerable:!0,get:function(){return s}});let r=e.r(91788),n="u"<typeof window,a=n?()=>{}:r.useLayoutEffect,o=n?()=>{}:r.useEffect;function s(e){let{headManager:t,reduceComponentsToState:i}=e;function s(){if(t&&t.mountedInstances){let e=r.Children.toArray(Array.from(t.mountedInstances).filter(
1Boolean));t.updateHead(i(e))}}return n&&(t?.mountedInstances?.add(e.children),s()),a(()=>(t?.mountedInstances?.add(e.children),()=>{t?.mountedInstances?.delete(e.children)})),a(()=>(t&&(t._pendingUpdate=s),()=>{t&&(t._pendingUpdate=s)})),o(()=>(t&&t._pendingUpdate&&(t._pendingUpdate(),t._pendingUpdate=null),()=>{t&&t._pendingUpdate&&(t._pendingUpdate(),t._pendingUpdate=null)})),null}},80963,(e,t,i)=>{"use strict";Object.defineProperty(i,"__esModule",{value:!0});var r={default:function(){return f},defaultHead:function(){return p}};for(var n in r)Object.defineProperty(i,n,{enumerable:!0,get:r[n]});let a=e.r(41705),o=e.r(52456),s=e.r(91398),l=o._(e.r(91788)),c=a._(e.r(94941)),u=e.r(13584);function p(){return[(0,s.jsx)("meta",{charSet:"utf-8"},"charset"),(0,s.jsx)("meta",{name:"viewport",content:"width=device-width"},"viewport")]}function d(e,t){return"string"==typeof t||"number"==typeof t?e:t.type===l.default.Fragment?e.concat(l.default.Children.toArray(t.props.children).reduce((e,t)=>"string"==typeof t||"number"==typeof t?e:e.concat(t),[])):e.concat(t)}e.r(94470);let m=["name","httpEquiv","charSet","itemProp"];function h(e){let t,i,r,n;return e.reduce(d,[]).reverse().concat(p().reverse()).filter((t=new Set,i=new Set,r=new Set,n={},e=>{let a=!0,o=!1;if(e.key&&"number"!=typeof e.key&&e.key.indexOf("$")>0){o=!0;let i=e.key.slice(e.key.indexOf("$")+1);t.has(i)?a=!1:t.add(i)}switch(e.type){case"title":case"base":i.has(e.type)?a=!1:i.add(e.type);break;case"meta":for(let t=0,i=m.length;t<i;t++){let i=m[t];if(e.props.hasOwnProperty(i))if("charSet"===i)r.has(i)?a=!1:r.add(i);else{let t=e.props[i],r=n[i]||new Set;("name"!==i||!o)&&r.has(t)?a=!1:(r.add(t),n[i]=r)}}}return a})).reverse().map((e,t)=>{let i=e.key||t;return l.default.cloneElement(e,{key:i})})}let f=function({children:e}){let t=(0,l.useContext)(u.HeadManagerContext);return(0,s.jsx)(c.default,{reduceComponentsToState:h,headManager:t,children:e})};("function"==typeof i.default||"object"==typeof i.default&&null!==i.default)&&void 0===i.default.__esModule&&(Object.defineProperty(i.default,"__esModule",{value:!0}),Object.assign(i.default,i),t.exports=i.default)},58678,(e,t,i)=>{t.exports=e.r(80963)},12117,(e,t,i)=>{t.exports=JSON.parse('{"name":"entaku","role":"Mobile App Developer","description":"iOSリードエンジニア。Swift / Kotlin / Go を使ったモバイルアプリ開発が得意。TVer・Voicy 等でリードエンジニアとして活躍。","githubUsername":"entaku0818","twitterUrl":"https://twitter.com/entaku_0818","twitterId":"@entaku_0818","githubUrl":"https://github.com/entaku0818","siteUrl":"https://entaku.dev","summary":["2013年からソフトウェア開発に携わるモバイルアプリエンジニア。SIerでインフラ・Web開発を経験した後、iOS / Android に軸足を移しました。","現在は株式会社TVerのiOSリードエンジニア。The Composable Architecture による再生基盤のリプレイスと広告連携を担当し、2026年ミラノ冬季オリンピックの配信にも対応しました。","前職のVoicyでは、外注主体だった収録アプリの開発を内製化。新規モジュールでテストカバレッジ80%以上、リリース後の不具合報告を約40%削減しました。","個人ではシンプル録音・読み上げナレーター・ClipKit・Festory など複数のアプリを配信中。アプリだけでなく Go / Firebase のバックエンドやオンデバイスAIまで一人で作っています。","iOSDC JAPAN で4回登壇(2020〜2024)。Swift愛好会の運営、DroidKaigi のスタッフとしてコミュニティにも関わっています。"],"timeline":[{"period":"2025/01 - 現在","title":"株式会社TVer","detail":"iOSリードエンジニア。動画再生
1基盤(TCA)と広告連携"},{"period":"2025 - 2026","title":"個人開発で新作を相次いでリリース","detail":"ClipKit・Speedmeter・Slumber・Festory"},{"period":"2021/12 - 2024/12","title":"株式会社Voicy","detail":"iOSエンジニア兼チームリーダー。収録アプリの内製化とアーキテクチャ刷新"},{"period":"2019/04 - 2021/11","title":"CBCloud","detail":"モバイルエンジニア → チームリーダー。PickGo / SmaRyuTruck"},{"period":"2018/02 - 2019/03","title":"Lifesports","detail":"スポーツマッチングアプリの開発"},{"period":"2013/09 - 2018/02","title":"株式会社エーピーコミュニケーションズ","detail":"ネットワークエンジニアからスタートし、Web・業務システム開発へ"}],"techStack":[{"label":"iOS","items":["Swift","SwiftUI","UIKit","TCA","Combine","CoreData / SwiftData","CloudKit","WidgetKit","Live Activities","App Intents","HealthKit"]},{"label":"Android","items":["Kotlin","Jetpack Compose","Room","WorkManager","ExoPlayer"]},{"label":"音声・動画","items":["AVFoundation / AVPlayer","Core Audio","Speech","Agora SDK"]},{"label":"Backend","items":["Go","Cloud Run","Firebase Functions","Firestore","Supabase","Rails","Laravel"]},{"label":"AI","items":["Gemini API","Claude API","WhisperKit","MLX Swift","Foundation Models","Claude Code"]},{"label":"CI/CD・運用","items":["GitHub Actions","fastlane","Xcode Cloud","Crashlytics","RevenueCat","AdMob"]}],"specialties":["モバイルアプリの開発、アーキテクチャ設計","音声配信 / ライブ配信の仕組ã
1¿ä½œã‚Š","事業マインドを持ったプロダクト開発 — 技術だけでなく事業成長の視点から施策を考え、実行できるエンジニアを目指している"],"talks":[{"event":"iOSDC JAPAN 2024","title":"Core Audio","links":[{"label":"詳細","url":"https://fortee.jp/iosdc-japan-2024/proposal/8c6f01d9-8fd3-4d5e-afef-f29d561f3c39"}]},{"event":"iOSDC JAPAN 2022","title":"音声配信アプリにおけるiOSを使った音声配信の全てと裏側","links":[{"label":"詳細","url":"https://fortee.jp/iosdc-japan-2022/proposal/ee2ab807-9053-4779-84f9-ec9951f7cfc0"}]},{"event":"iOSDC JAPAN 2021","title":"既存のネイティブアプリをFlutterへリプレイスする方法","links":[{"label":"詳細","url":"https://iosdc.jp/2021/"},{"label":"Speaker Deck","url":"https://speakerdeck.com/entaku/pickgo-for-partnerfalseyi-xing-fang-fa-karaxue-hu-ji-cun-falseneiteihuahuriwoflutterherihureisusurufang-fa"}]},{"event":"iOSDC JAPAN 2020","title":"DroidKaigiの公式アプリで始めるiOSアプリのOSSコミッターへの道","links":[{"label":"詳細","url":"https://fortee.jp/iosdc-japan-2020/proposal/c3c70224-08cf-48d7-b5e5-a3d4589737fa"},{"label":"Speaker Deck","url":"https://speakerdeck.com/entaku/droidkaigifalsegong-shi-ahuriteshi-meru-iosahuriosskomitutahefalsedao"}]},{"event":"Vue.js アーキテクチャリング勉強会","title":"Vue / Vuex のアーキテクチャを完全に理解した","links":[{"label":"詳細","url":"https://cw-engineers.connpass.com/event/146975/"},{"label":"Speaker Deck","url":"https://speakerdeck.com/entaku/vue-vuex-falseakitekutiyawan-quan-nili-jie-sita"}]}],"communities":[{"name":"Swift愛好会","role":"運営メンバー","url":"https://love-swift.connpass.com/","linkLabel":"connpass","description":"Swiftエンジニア2,200名以上が参加するコミュニティの運営に携わり、月1〜2回のペースで勉強会を企画・開催(vol.98まで継続中)。"},{"name":"DroidKaigi","role":"スタッフ","url":"https://droidkaigi.jp/","linkLabel":"Website","description":"Android開発者向けカンファレンス「DroidKaigi」のスタッフとして運営に参加。"}],"experiences":[{"company":"TVer","period":"2025/01 - 現在","url":"https://tver.jp/","overview":"日本最大級の民放公式テレビポータル「TVer」にて、iOSリードエンジニアとして参画。動画再生まわりの体験と、その周辺の広告連携の開発を担当している。","highlights":[{"title":"動画再生と広告収益化","points":["ライブ配信・VODの双方でプレイヤー機能の開発を担当し、再生まわりの広告連携も実装","動画再生関連のコードをTCA(The Composable Architecture)でリプレイスし、保守性・テスタビリティを改善"]},{"title":"大規模ライブイベントの配信対応","points":["2026年ミラノ冬季オリンピックの配信に対応し、五輪特集トップ画面・特集UIを新規実装","外部に固定された締め切りのもとで、ライブ配信とVODの双方をカバーする再利用可能なプレイヤーモジュールを構築"]},{"title":"Xcode Cloudによる CI環境の構築","points":["テストは存在していたがCI環境が未整備だった状況を改善","Xcode Cloudを導入し、PR単位での自動テスト実行とビルド検証を実現。iOSチーム全体の開発サイクルを安定化"]},{"title":"ウォークスルー画面の刷新","points":["既存のオンボーディングフローを全面的に見直し、ユーザーが直感的にアプリを理解できるUIに改善"]}],"tech":[{"label":"言語","items":["Swift"]},{"label":"フレームワーク","items":["SwiftUI","UIKit","Combine","TCA"]},{"label":"動画再生","items":["AVFoundation","AVPlayer"]},{"label":"CI/CD","items":["Xcode Cloud"]}]},{"company":"Voicy","period":"2021/12 - 2024/12","url":"https://voicy.jp/","overview":"音声配信プラットフォーム「Voicy」にて、iOSエンジニア兼チームリーダーとして3年間従事。パーソナリティ向け収録アプリの開発責任者として、外注主体だった開発体制の内製化からアーキテクチャ刷新、新機能開発までをリードした。","highlights":[{"title":"アプリ開発の内製化とコード品質の抜本的改善","points":["創業当初から外注で開発されていたアプリはUnitTestがなã
1ã€ãƒã‚°æ¤œçŸ¥ãŒæ‰‹å‹•テストに依存していた","マルチモジュールアーキテクチャを導入し、新規モジュールでテストカバレッジ80%以上を達成","iOS/Androidエンジニア4名と連携し、機能開発を止めずに段階的なリファクタリングを推進。結果としてリリース後の不具合報告が約40%減少"]},{"title":"コラボ収録機能の新規開発","points":["パーソナリティ同士がリモートで収録できる「コラボ収録」機能を設計・開発","アジャイル開発を導入し、PdMと週次で成果物をレビュー。開発途中段階でパーソナリティ5名に検証協力を依頼し、フィードバックを元にUIを改善","リリース後、新たに100名以上のパーソナリティがコラボ収録を利用。パーソナリティ間の交流促進とコンテンツの多様化に貢献"]},{"title":"生放送機能の開発","points":["Agoraを活用したリアルタイム音声配信機能をiOS/Android両プラットフォームで実装","生放送の作成・開始・予約画面をフルスクラッチで開発し、配信前チェック機能(ネットワーク状態・権限確認)も実装","低遅延配信とリスナー参加機能を実現し、パーソナリティとリスナーの双方向コミュニケーションを可能に"]},{"title":"PL限定コメント機能(Android)","points":["パーソナリティ限定コメントの表示・制御機能を設計・実装","放送種別の取得・判定ロジックを追加し、限定機能の出し分けを実現"]},{"title":"Material3移行(Android収録アプリ)","points":["UIコンポーネントをMaterial3に統一し、デザインシステムの一貫性を確保"]},{"title":"バックエンド(Go)API開発","points":["コラボ収録機能向けにOpenAPIでセッションAPIを設計・実装","Agoraを活用した生放送用DBの構築とAPI整備","エッセンシャルルーム機能(参加承認・拒否・退出)のAPI開発"]},{"title":"採用活動","points":["エンジニア採用の1次面談を担当し、技術力とカルチャーフィットの両面から候補者を評価","3名の採用に貢献し、チームの開発体制強化に寄与"]}],"tech":[{"label":"言語","items":["Swift","Kotlin","Go"]},{"label":"フレームワーク","items":["SwiftUI","UIKit","RxSwift","Jetpack Compose"]},{"label":"音声・配信","items":["AVFoundation","Agora SDK","ExoPlayer"]},{"label":"アーキテクチャ","items":["マルチモジュール","MVVM"]},{"label":"その他","items":["XCTest","GitHub Actions"]}]},{"company":"CBCloud","period":"2019/04 - 2021/11","url":"https://cb-cloud.com/","overview":"配送マッチングプラットフォーム「PickGo」および一般貨物運送事業者向けSaaS「SmaRyuTruck」の開発に従事。モバイルエンジニアからチームリーダーへとステップアップし、開発と顧客折衝の両面で貢献。","highlights":[{"title":"SmaRyuTruck新規開発でのチームリーダー就任","points":["エンジニア中心のチームで途中からチームリーダーに志願","社長がプロダクトオーナーを務める中、開発優先順位の調整とステークホルダー間の橋渡しを担当","顧客先へ直接出向き、運送業務の現場課題をヒアリング。その知見をプロダクト要件に反映させ、サービス導入を推進","[一般貨物向け配送依頼ページ](https://pickgo.town/)の新規作成を担当"]},{"title":"PickGoチーム再建と機能開発","points":["チームメンバーの退職が相次ぎ、開発者が自分一人となる状況を経験","短期間で既存コードベースと業務フローを把握し、開発を継続","[JAL航空便連携](https://press.jal.co.jp/ja/release/202103/005969.html)を要件定義からリリースまで一人で完遂","季節変動に対応した配送料金の動的変更機能を実装"]},{"title":"採用活動","points":["エンジニア採用の1次面談を担当"]}],"deliverables":[{"name":"SmaRyuTruck","url":"https://smaryu.town/truck/","de
1scription":"一般貨物運送事業者向けSaaS"},{"name":"PickGo","url":"https://pickgo.town/","description":"配送マッチングプラットフォーム"},{"name":"買い物代行サービス","url":"https://pickgo.town/consumer/shopping"}],"tech":[{"label":"モバイル","items":["Swift","Kotlin"]},{"label":"フロントエンド","items":["Nuxt.js"]},{"label":"バックエンド","items":["Rails"]}]},{"company":"Lifesports","period":"2018/02 - 2019/03","url":"https://lifesports.jp/","overview":"スポーツマッチングサービスのモバイルアプリ開発に従事。ユーザー獲得に向けた機能改善とコミュニティ機能の新規開発を担当。","highlights":[{"points":["ユーザー数増加を目的とした既存機能の改善とUI/UX向上","スポーツ愛好者同士をつなぐコミュニティ機能を設計・実装"]}],"tech":[{"label":"モバイル","items":["Swift","Kotlin"]},{"label":"バックエンド","items":["Laravel"]}]},{"company":"株式会社エーピーコミュニケーションズ","period":"2013/09 - 2018/02","url":"https://www.ap-com.co.jp/","overview":"ネットワーク系SIerにて、インフラから開発まで幅広い領域を経験。ネットワークエンジニアとしてキャリアをスタートし、開発部門への異動を経てフルスタックなスキルを習得。","highlights":[{"points":["国内大手企業向けネットワーク認証システムの運用・保守を担当","大規模システムリプレイスプロジェクトに参画し、インフラ構築から移行作業までを遂行","開発部門異動後、教育事業会社・娯楽事業のシステム保守・リプレイスを担当"]}],"tech":[{"label":"バックエンド","items":["Java (Spring)","PHP (Laravel)"]},{"label":"データベース","items":["MySQL"]},{"label":"インフラ","items":["ネットワーク構築・運用"]}]}],"sideProjects":[{"title":"ishin モバイルエンジニア","period":"2025/07 - 2026/03","overview":"AIアバターが参加できる音声トークルームサービス「ishin」の開発に参加。モバイルアプリ・バックエンドAPI・Web の3領域にまたがって機能開発を担当した。","points":["React Native(Expo SDK 52)でトークルーム参加UI・波紋アニメーション・AIアバター選択機能などを実装","AIアバターのバッジ表示・自動応答トリガー・メッセージ表示キューシステムを設計・開発","キニナル(フォロー)機能・ブロック機能をモバイルとAPIの両側で実装","Firebase Analytics / BigQuery連携によるトークルーム参加・作成イベントのトラッキング基盤を構築","App Tracking Transparency(ATT)対応・GIF圧縮・EASビルドサイズ最適化などApp Store申請対応を担当","ishin-web(Next.js)でOGP画像生成を最適化。フォントキャッシュとメモリキャッシュの導入により初回11秒から1.75秒へ6.6倍高速化を達成","GoバックエンドAPIでハートビートAPI・BigQuery同期・AIアバター管理などのエンドポイントを開発"],"tech":["React Native","Expo","TypeScript","Go","Next.js","Firebase","BigQuery","Auth0","EAS"]},{"title":"linq社 iOSエンジニア","period":"2023/04 - 2024/04","url":"https://linq.co.jp/","siteName":"linq","overview":"位置情報共有アプリ「whoo」のiOS開発に参画。","points":["UIKitからSwiftUIへの段階的移行を推進","Map上で友人にスタンプを送る機能、メッセージ機能を新規開発","ユーザー同士の交流を促進するインタラクティブなアニメーションを実装"],"tech":["SwiftUI","UIKit","MapKit","CoreLocation","Core Animation"]},{"title":"SOXAI ヘルスケアアプリ","period":"2022/11 - 2023/03","url":"https://soxai.co.jp/","siteName":"SOXAI","overview":"ヘルスケアアプリのモバイル開発に参画。","points":["マルチプラットフォームフレームワーク[Quasar](https://quasar.dev/)を用いた開発","ヘルスデータを可視化するグラフ画面の設計・実装"]},{"title":"nossa360 Androidエンジニア","period":"2020/04 - 2021/03","url":"https://lp.nossa360biz.com/","siteName":"nossa360","overview":"建築・不動産業界向け360度撮影アプリ「nossa360」のAndroidç‰
1ˆé–‹ç™ºã‚’担当。","points":["Camera2 APIを活用した360度撮影機能を実装","画像処理の最適化によりアプリのパフォーマンスを向上","建築現場での利用に特化したUI/UX改善を実施"],"tech":["Kotlin","Camera2 API","画像処理"]}],"personalApps":[{"name":"シンプル録音 - 音声レコーダー","tagline":"ワンタップで始まる音声レコーダー","platform":"iOS / Android","overview":"ワンタップで録音を始められる音声レコーダー。バックグラウンド録音、プレイリスト、ロック画面での録音時間表示などを実装し、英語・日本語・中国語・スペイン語などの多言語に対応している。","featured":true,"screenshot":"/apps/simple-recorder.webp","metrics":["App Store 評価 4.2 / 229ä»¶","iOS・Android の両方を個人で開発・運用"],"tech":["Swift","SwiftUI","AVFoundation","CloudKit","Kotlin","Android Jetpack","Room","WorkManager"],"links":[{"label":"App Store","url":"https://apps.apple.com/us/app/simple-voice-recorder-audio/id6443528409"},{"label":"Google Play","url":"https://play.google.com/store/apps/details?id=com.entaku.simpleRecord"}]},{"name":"ClipKit - コピー履歴管理","tagline":"コピーした内容を、バックグラウンドでも取りこぼさない","platform":"iOS","overview":"コピーした内容を自動で保存するクリップボード履歴アプリ。iOS 16以降のバックグラウンド制限を Picture in Picture で回避してクリップボードを監視し続ける仕組みを自作した。履歴の検索・ピン留め・テキスト変換、ウィジェットとキーボード拡張からの呼び出しにも対応している。","featured":true,"screenshot":"/apps/clipkit.webp","metrics":["App Store「クリップボード」検索で3位","PiPを使ったバックグラウンド監視を自作","TCA + RevenueCat でサブスクリプションを実装"],"tech":["Swift","SwiftUI","TCA","AVKit (PiP)","CloudKit","WidgetKit","Keyboard Extension","RevenueCat"],"links":[{"label":"App Store","url":"https://apps.apple.com/jp/app/id6759832862"}]},{"name":"読み上げナレーター","tagline":"書いたテキストを、そのまま声にする","platform":"iOS","overview":"入力したテキストを読み上げる音声合成アプリ。iOSの音声まわりのAPIを実際のプロダクトで試すために作り、リリース後もフィードバックを見ながら改善を続けている。","featured":true,"screenshot":"/apps/narrator.webp","tech":["Swift","SwiftUI","AVFoundation","Speech","Core Audio"],"links":[{"label":"App Store","url":"https://apps.apple.com/jp/app/読み上げナレーター-声で読み上げ/id6478449537"}]},{"name":"シンプル文字起こし","tagline":"録音も会議メモも、その場でテキストに","platform":"iOS","overview":"音声をテキストに変換する文字起こしアプリ。オンデバイスで音声認識を行い、録音データや会議メモの書き起こしに使える。","metrics":["音声認識をオンデバイスで実行し、音声を外部に送らない"],"tech":["Swift","SwiftUI","Core ML","Speech","Natural Language"],"links":[{"label":"App Store","url":"https://apps.apple.com/jp/app/id6504149514"}]},{"name":"Slumber - 睡眠記録・スマートアラーム","tagline":"眠りをスコアにして、気持ちよく起きる","platform":"iOS","overview":"睡眠の記録と分析を行うアプリ。日の出時刻・体動・画面の明るさの3条件から起床タイミングを判定するスマートアラームを実装し、AlarmKitでサイレントモードでも鳴るようにしている。毎朝の睡眠スコアと週間レポート、ロック画面ウィジェットを提供する。","metrics":["AlarmKitでサイレントモードでも鳴るアラームを実装","CoreMotion・CoreLocation・輝度の3条件で起床タイミングを判定"],"tech":["Swift","SwiftUI","AlarmKit","CoreMotion","CoreLocation","HealthKit","WeatherKit","WidgetKit"],"links":[{"label":"App Store","url":"https://apps.apple.com/jp/app/id6770225144"}]}
1,{"name":"Festory - フェス・ライブ参戦記録","tagline":"セトリごと残す、参戦の記録帳","platform":"iOS","overview":"フェスやライブへの参加を記録するアプリ。セットリストやアンコールの記録、行きたいイベントのウィッシュリスト、国内主要フェスのラインナップ情報を扱う。iOSアプリに加えてGoのAPIとランディングページも自分で作り、Cloud Run と Vercel で運用している。","metrics":["iOSアプリ・Go API・Webまでを一人で設計して運用"],"tech":["Swift","SwiftUI","Go","Cloud Run","React","Vite","Firebase"],"links":[{"label":"App Store","url":"https://apps.apple.com/jp/app/id6763427429"}]}]}')},69065,(e,t,i)=>{t.exports=JSON.parse('{"name":"entaku","role":"Senior iOS Engineer — Video Streaming & Ad Tech","description":"Senior iOS engineer specializing in video streaming and ad monetization. Currently iOS lead at TVer, one of Japan\'s largest free ad-supported streaming services.","githubUsername":"entaku0818","twitterUrl":"https://twitter.com/entaku_0818","twitterId":"@entaku_0818","githubUrl":"https://github.com/entaku0818","siteUrl":"https://entaku.dev/en","summary":["Mobile app engineer writing software since 2013. I started out on infrastructure and web systems at an SIer, then moved my focus to iOS and Android.","iOS lead at TVer, one of Japan\'s largest free ad-supported streaming services. I rebuilt the playback stack with The Composable Architecture, work on the ad integration around it, and supported streaming of the Milano Cortina 2026 Winter Olympics.","At Voicy I brought the recording app in-house from an outsourced team, reaching 80%+ test coverage on new modules and cutting post-release bug reports by roughly 40%.","On my own I ship several apps, including Simple Voice Recorder, Voice Narrator, ClipKit and Festory — along with the Go / Firebase backends and on-device AI behind them.","Four talks at iOSDC Japan (2020–2024). I help organize Swift愛好会 (Swift Lovers) and have volunteered as DroidKaigi staff."],"timeline":[{"period":"2025/01 - Present","title":"TVer","detail":"iOS lead — video playback (TCA) and ad integration"},{"period":"2025 - 2026","title":"New personal apps","detail":"ClipKit, Speedmeter, Slumber and Festory"},{"period":"2021/12 - 2024/12","title":"Voicy","detail":"iOS engineer & team lead — in-housing and re-architecting the recording app"},{"period":"2019/04 - 2021/11","title":"CBCloud","detail":"Mobile engineer → team lead on PickGo / SmaRyuTruck"},{"period":"2018/02 - 2019/03","title":"Lifesports","detail":"Mobile apps for a sports matching service"},{"period":"2013/09 - 2018/02","title":"AP Communications","detail":"Started as a network engineer, then moved into web and business systems"}],"techStack":[{"label":"iOS","items":["Swift","SwiftUI","UIKit","TCA","Combine","CoreData / SwiftData","CloudKit","WidgetKit","Live Activities","App Intents","HealthKit"]},{"label":"Android","items":["Kotlin","Jetpack Compose","Room","WorkManager","ExoPlayer"]},{"label":"Audio & Video","items":["AVFoundation / AVPlayer","Core Audio","Speech","Agora SDK"]},{"label":"Backend","items":["Go","Cloud Run","Firebase Functions","Firestore","Supabase","Rails","Laravel"]},{"label":"AI","items":["Gemini API","Claude API","WhisperKit","MLX Swift","Foundation Models","Claude Code"]},{"label":"CI/CD & Ops","items":["GitHub Actions","fastlane","Xcode Cloud","Crashlytics","RevenueCat","AdMob"]}],"specialties":["Video playback and ad monetization on mobile — player architecture, live and VOD","Audio and live streaming infrastructure","Product-minded engineering — deciding what to build from a business perspective, not only a technical one"],"talks":[{"event":"iOSDC Japan 2024","title":"Core Audio","links":[{"label":"Details","url":"https://fortee.jp/iosdc-japan-2024/proposal/8c6f01d9-8fd3-4d5e-afef-f29d561f3c39"}]},{"event":"iOSDC Japan 2022","title":"Everything behind audio broadcasting on iOS","links":[{"label":"Details","url":"https://fortee.jp/iosdc-japan-2022/proposal/ee2ab807-9053-4779-84f9-ec9951f7cfc0"}]},{"event":"iOSDC Japan 2021","title":"How to replace an existing native app with Flutter","links":[{"label":"Details","url":"https://iosdc.jp/2021/"},{"label":"Speaker Deck","url":"https://speakerdeck.com/entaku/pickgo-for-partnerfalseyi-xing-fang-fa-karaxue-hu-ji-cun-falseneiteihuahuriwoflutterherihureisusurufang-fa"}]},{"event":"iOSDC Japan 2020","title":"Becoming an iOS OSS contributor through the DroidKaigi official app","links":[{"label":"Details","url":"https://fortee.jp/iosdc-japan-2020/proposal/c3c70224-08cf-48d7-b5e5-a3d4589737fa"},{"label":"Speaker Deck","url":"https://speakerdeck.com/entaku/droidkaigifalsegong-shi-ahuriteshi-meru-iosahuriosskomitutahefalsedao"}]},{"event":"Vue.js Architecting Meetup","title":"Understanding Vue / Vuex architecture, completely","links":[{"label":"Details","url":"https://cw-engineers.connpass.com/event/146975/"},{"label":"Speaker Deck","url":"https://speakerdeck.com/entaku/vue-vuex-falseakitekutiyawan-quan-nili-jie-sita"}]}],"communities":[{"name":"Swift愛好会 (Swift Lovers)","role":"Organizer","url":"https://love-swift.connpass.com/","linkLabel":"connpass","description":"Organizer of a Japanese Swift community with 2,200+ engineers, running meetups once or twice a month (98 editions and counting)."},{"name":"DroidKaigi","role":"Staff","url":"https://droidkaigi.jp/","linkLabel":"Website","description":"Staff member of DroidKaigi, the largest Android developer conference in Japan."}],"experiences":[{"company":"TVer","period":"Jan 2025 - Present","url":"https://tver.jp/","overview":"iOS lead engineer at TVer, one of Japan\'s largest free ad-supported streaming services, working on the video playback experience and the ad integration around it.","highlights":[{"title":"Video playback and ad monetization","points":["Work on player features for both live and on-demand streaming, including the ad integration layer around playback","Replaced playback-related code with The Composable Architecture, improving maintainability and testability"]},{"title":"Large-scale live event delivery","points":["Delivered the streaming experience for the Milano Cortina 2026 Winter Olympics, including a dedicated top screen and event UI","Built reusable player modules covering both live and VOD playback under a hard, externally fixed deadline"]},{"title":"CI with Xcode Cloud","points":["Tests existed but there was no CI. Introduced Xcode Cloud so every pull request runs automated tests and build verification","Stabilized the development cycle for the whole iOS team"]},{"title":"Onboarding redesign","points":["Rebuilt the walkthrough flow so new users understand the app immediately"]}],"tech":[{"label":"Language","items":["Swift"]}
1,{"label":"Frameworks","items":["SwiftUI","UIKit","Combine","TCA"]},{"label":"Playback","items":["AVFoundation","AVPlayer"]},{"label":"CI/CD","items":["Xcode Cloud"]}]},{"company":"Voicy","period":"Dec 2021 - Dec 2024","url":"https://voicy.jp/","overview":"iOS engineer and team lead at Voicy, an audio streaming platform. As the owner of the recording app for creators, I led the move from outsourced development to an in-house team, the architecture overhaul, and new feature development.","highlights":[{"title":"In-housing development and rebuilding code quality","points":["The app had been outsourced since the company\'s founding, had no unit tests, and relied on manual QA to catch bugs","Introduced a multi-module architecture and reached 80%+ test coverage on new modules","Drove incremental refactoring with four iOS/Android engineers without pausing feature work, cutting post-release bug reports by about 40%"]},{"title":"Collaborative recording","points":["Designed and built a feature that lets creators record together remotely","Introduced agile practices, reviewing increments weekly with the PdM and running mid-development validation with five creators","100+ creators adopted collaborative recording after launch"]},{"title":"Live broadcasting","points":["Implemented real-time audio broadcasting on both iOS and Android using Agora","Built the creation, start and scheduling screens from scratch, including pre-broadcast checks for network state and permissions","Enabled low-latency delivery and listener participation for two-way communication"]},{"title":"Backend (Go) API development","points":["Designed and implemented the session API for collaborative recording with OpenAPI","Built the live broadcasting database and its APIs around Agora","Developed the APIs for room admission, rejection and exit flows"]},{"title":"Android work","points":["Designed and implemented creator-only comment display and control, including broadcast-type detection","Migrated the Android recording app\'s UI components to Material 3"]},{"title":"Hiring","points":["Ran first-round engineering interviews, assessing both technical skill and culture fit","Contributed to three hires that strengthened the team"]}],"tech":[{"label":"Languages","items":["Swift","Kotlin","Go"]},{"label":"Frameworks","items":["SwiftUI","UIKit","RxSwift","Jetpack Compose"]},{"label":"Audio & streaming","items":["AVFoundation","Agora SDK","ExoPlayer"]},{"label":"Architecture","items":["Multi-module","MVVM"]},{"label":"Other","items":["XCTest","GitHub Actions"]}]},{"company":"CBCloud","period":"Apr 2019 - Nov 2021","url":"https://cb-cloud.com/","overview":"Worked on PickGo, a delivery matching platform, and SmaRyuTruck, a SaaS for freight carriers. Moved from mobile engineer to team lead, contributing on both development and customer-facing work.","highlights":[{"title":"Team lead on the SmaRyuTruck launch","points":["Volunteered to take over as team lead partway through the project","Coordinated development priorities and bridged stakeholders while the CEO acted as product owner","Visited customers directly to understand real logistics operations and fed those findings into product requirements","Built the [freight delivery request page](https://pickgo.town/)"]},{"title":"Rebuilding the PickGo team","points":["After a wave of departures I was the only remaining developer on the product","Got up to speed on the existing codebase and business flows quickly enough to keep shipping","Delivered the [JAL air freight integration](https://press.jal.co.jp/ja/release/202103/005969.html) alone, from requirements to release","Implemented dynamic delivery pricing that responds to seasonal demand"]},{"title":"Hiring","points":["Ran first-round engineering interviews"]}],"deliverables":[{"name":"SmaRyuTruck","url":"https://smaryu.town/truck/","description":"SaaS for freight carriers"},{"name":"PickGo","url":"https://pickgo.town/","description":"Delivery matching platform"},{"name":"Shopping proxy service","url":"https://pickgo.town/consumer/shopping"}],"tech":[{"label":"Mobile","items":["Swift","Kotlin"]},{"label":"Frontend","items":["Nuxt.js"]},{"label":"Backend","items":["Rails"]}]},{"company":"Lifesports","period":"Feb 2018 - Mar 2019","url":"https://lifesports.jp/","overview":"Mobile app development for a sports matching service, focused on feature improvements for user growth and a new community feature.","highlights":[{"points":["Improved existing features and UI/UX to grow the user base","Designed and implemented a community feature connecting sports enthusiasts"]}
1],"tech":[{"label":"Mobile","items":["Swift","Kotlin"]},{"label":"Backend","items":["Laravel"]}]},{"company":"AP Communications","period":"Sep 2013 - Feb 2018","url":"https://www.ap-com.co.jp/","overview":"Started my career at a network-focused SIer, covering everything from infrastructure to application development after moving to the development division.","highlights":[{"points":["Operated and maintained a network authentication system for a major Japanese enterprise","Joined a large-scale system replacement project, handling infrastructure setup through migration","After moving to the development division, maintained and replaced systems for education and entertainment businesses"]}],"tech":[{"label":"Backend","items":["Java (Spring)","PHP (Laravel)"]},{"label":"Database","items":["MySQL"]},{"label":"Infrastructure","items":["Network design & operations"]}]}],"sideProjects":[{"title":"ishin — Mobile Engineer","period":"Jul 2025 - Mar 2026","overview":"Contributed to ishin, a voice talk room service where AI avatars can join conversations. Worked across three areas: the mobile app, the backend API and the web front end.","points":["Built talk room join UI, ripple animations and AI avatar selection in React Native (Expo SDK 52)","Designed AI avatar badges, auto-response triggers and a message display queue system","Implemented follow and block features on both the mobile app and the API","Built event tracking for room joins and creations via Firebase Analytics and BigQuery","Handled App Store submission work: App Tracking Transparency, GIF compression and EAS build size optimization","Optimized OGP image generation in ishin-web (Next.js): 6.6x faster (11s → 1.75s) via font and memory caching","Developed heartbeat, BigQuery sync and AI avatar management endpoints in the Go backend"],"tech":["React Native","Expo","TypeScript","Go","Next.js","Firebase","BigQuery","Auth0","EAS"]},{"title":"linq — iOS Engineer","period":"Apr 2023 - Apr 2024","url":"https://linq.co.jp/","siteName":"linq","overview":"Joined iOS development for whoo, a location sharing app.","points":["Drove the incremental migration from UIKit to SwiftUI","Built new features for sending stamps to friends on the map, and messaging","Implemented interactive animations that encourage user-to-user interaction"],"tech":["SwiftUI","UIKit","MapKit","CoreLocation","Core Animation"]},{"title":"SOXAI Healthcare App","period":"Nov 2022 - Mar 2023","url":"https://soxai.co.jp/","siteName":"SOXAI","overview":"Joined mobile development for a healthcare app.","points":["Developed with [Quasar](https://quasar.dev/), a cross-platform framework","Designed and implemented the graph screens visualizing health data"]},{"title":"nossa360 — Android Engineer","period":"Apr 2020 - Mar 2021","url":"https://lp.nossa360biz.com/","siteName":"nossa360","overview":"Android development for nossa360, a 360-degree camera app for the construction and real estate industries.","points":["Implemented 360-degree capture using the Camera2 API","Improved app performance through image processing optimization","Refined the UI/UX for use on construction sites"],"tech":["Kotlin","Camera2 API","Image processing"]}],"personalApps":[{"name":"Simple Voice Recorder","tagline":"A voice recorder that starts with one tap","platform":"iOS / Android","overview":"A voice recorder built around one-tap recording. It supports background recording, playlists and recording time on the lock screen, and ships in English, Japanese, Chinese and Spanish among others.","featured":true,"screenshot":"/apps/simple-recorder.webp","metrics":["Rated 4.2 from 229 App Store reviews","Both the iOS and Android apps built and maintained solo"],"tech":["Swift","SwiftUI","AVFoundation","CloudKit","Kotlin","Android Jetpack","Room","WorkManager"],"links":[{"label":"App Store","url":"https://apps.apple.com/us/app/simple-voice-recorder-audio/id6443528409"},{"label":"Google Play","url":"https://play.google.com/store/apps/details?id=com.entaku.simpleRecord"}]},{"name":"ClipKit — Clipboard Manager","tagline":"Keeps watching the clipboard, even in the background","platform":"iOS","overview":"A clipboard manager that saves what you copy automatically. To keep watching the clipboard under the background limits introduced in iOS 16, I built the monitoring on top of Picture in Picture. It also covers search, pinning and text conversion, reachable from a widget and a keyboard extension.","featured":true,"screenshot":"/apps/clipkit.webp","metrics":["Ranks 3rd for “clipboard” on the Japanese App Store","Backgroun
1d monitoring built on Picture in Picture","Subscriptions with TCA and RevenueCat"],"tech":["Swift","SwiftUI","TCA","AVKit (PiP)","CloudKit","WidgetKit","Keyboard Extension","RevenueCat"],"links":[{"label":"App Store","url":"https://apps.apple.com/jp/app/id6759832862"}]},{"name":"Voice Narrator","tagline":"Turns whatever you type into speech","platform":"iOS","overview":"A text-to-speech app. I built it to try the iOS audio and speech APIs on a real product, and keep improving it based on user feedback.","featured":true,"screenshot":"/apps/narrator.webp","tech":["Swift","SwiftUI","AVFoundation","Speech","Core Audio"],"links":[{"label":"App Store","url":"https://apps.apple.com/jp/app/読み上げナレーター-声で読み上げ/id6478449537"}]},{"name":"Simple Transcription","tagline":"Recordings and meeting notes, as text","platform":"iOS","overview":"A transcription app that turns speech into text. Recognition runs on device, which makes it practical for recordings and meeting notes.","metrics":["Speech recognition runs on device, so audio never leaves it"],"tech":["Swift","SwiftUI","Core ML","Speech","Natural Language"],"links":[{"label":"App Store","url":"https://apps.apple.com/jp/app/id6504149514"}]},{"name":"Slumber — Sleep & Alarm","tagline":"Scores your sleep so you wake up well","platform":"iOS","overview":"A sleep tracking app. Its smart alarm picks a wake-up moment from three signals — sunrise time, body movement and screen brightness — and AlarmKit makes it ring even in silent mode. It reports a sleep score each morning, a weekly trend and a lock screen widget.","metrics":["AlarmKit alarms that ring through silent mode","Wake-up timing from CoreMotion, CoreLocation and screen brightness"],"tech":["Swift","SwiftUI","AlarmKit","CoreMotion","CoreLocation","HealthKit","WeatherKit","WidgetKit"],"links":[{"label":"App Store","url":"https://apps.apple.com/jp/app/id6770225144"}]},{"name":"Festory — Concert & Setlist Log","tagline":"A log of every festival, setlist included","platform":"iOS","overview":"An app for logging the festivals and concerts you attend, with setlists, encores, a wishlist of upcoming events and lineups for major Japanese festivals. Alongside the iOS app I built the Go API and the landing page, running on Cloud Run and Vercel.","metrics":["iOS app, Go API and web all designed and operated solo"],"tech":["Swift","SwiftUI","Go","Cloud Run","React","Vite","Firebase"],"links":[{"label":"App Store","url":"https://apps.apple.com/jp/app/id6763427429"}]}]}')},69711,1860,64711,e=>{"use strict";var t=e.i(91398);let i={ja:{overview:"概要",technicalBackground:"技術略歴",timeline:"年表",specialties:"得意なこと",talks:"登壇歴",communities:"コミュニティ活動",experience:"職歴",sideProjects:"Side Projects",personalApps:"個人開発",projectOverview:"プロジェクト概要",achievements:"実績・取り組み",deliverables:"主な成果物",tech:"利用技術",website:"Website",personalEyebrow:"About Me",personalTitle:"Personal",articlesTitle:"Articles",articlesCount:e=>`${e}件`,switchLangLabel:"English",nav:{apps:"個人開発",about:"できること",contact:"ご相談",home:"Home",personal:"Personal",articles:"Articles"},hero:{headline:"困りごとを道具に、日常をエンタメに。",stack:"iOS ・ Android ・ Go",lead:"企画から設計・実装、ストア申請、リリース後の運用まで。仕事ではiOSのリードエンジニアとして、個人ではこれを全部ひとりでやっています。",primaryCta:"開発のご相談",secondaryCta:"個人開発を見る",stats:e=>[{value:e,suffix:"本",label:"配信中の個人開発アプリ"},{value:4.2,decimals:1,label:"シンプル録音の App Store 評価(229件)"},{value:10,suffix:"年+",label:"ソフトウェア開発歴"}]}
1,apps:{eyebrow:"Personal Apps",title:"個人開発",lead:"仕事とは別に、自分で企画して App Store / Google Play に出しているアプリです。",unreleased:"未配信",highlights:"見どころ",placeholder:"ストア未配信"},about:{eyebrow:"About",title:"できること"},more:{eyebrow:"More",title:"これまでの仕事",lead:"職歴・登壇・コミュニティ活動は、見たいものだけ開いてください。"},contact:{eyebrow:"Contact",title:"開発のご相談",lead:"iOS / Android アプリの新規開発、既存アプリの改善、技術相談など。まずはざっくりした内容で構いません。",name:"お名前",namePlaceholder:"山田 太郎",email:"連絡先メールアドレス",emailPlaceholder:"[email protected]",budget:"ご予算感",budgetPlaceholder:"未定 / 〜50万円 / 50〜150万円 など",message:"ご相談内容",messagePlaceholder:"つくりたいもの、現在の状況、希望スケジュールなどをお書きください。",required:"必須",optional:"任意",submit:"送信する",submitting:"送信中…",success:"送信しました。ありがとうございます。",successDetail:"2営業日以内にご返信します。",invalid:"入力内容をご確認ください。",error:"送信に失敗しました。時間をおいてもう一度お試しください。",notConfigured:"ただいまフォームからの送信を準備中です。お手数ですが X または GitHub からご連絡ください。",rateLimited:"短時間に送信が続いています。しばらく時間をおいてからお試しください。",privacy:"いただいた内容は、ご相談への返信にのみ利用します。",honeypot:"この欄は入力しないでください",fieldError:e=>({required:"入力してください。",invalid_email:"メールアドレスの形式で入力してください。",too_long:"文字数が多すぎます。"})[e]},footerNote:"モバイルアプリ開発のご相談はいつでもどうぞ。"},en:{overview:"Overview",technicalBackground:"Tech stack",timeline:"Timeline",specialties:"What I do best",talks:"Talks",communities:"Community",experience:"Experience",sideProjects:"Side Projects",personalApps:"Personal Ap
1ps",projectOverview:"Project overview",achievements:"Highlights",deliverables:"Deliverables",tech:"Tech stack",website:"Website",personalEyebrow:"About Me",personalTitle:"Personal",articlesTitle:"Articles",articlesCount:e=>`${e} articles`,articlesNote:"The posts themselves are written in Japanese.",switchLangLabel:"日本語",nav:{apps:"Apps",about:"About",contact:"Contact",home:"Home",personal:"Personal",articles:"Articles"},hero:{headline:"Problems into tools. Days into entertainment.",stack:"iOS · Android · Go",lead:"From the first idea through architecture, implementation, App Store review and everything that comes after release. I lead iOS by day and do all of it myself on the side.",primaryCta:"Start a project",secondaryCta:"See the apps",stats:e=>[{value:e,label:"Personal apps on the stores"},{value:4.2,decimals:1,label:"App Store rating, Simple Voice Recorder (229 reviews)"},{value:10,suffix:"+",label:"Years building software"}]},apps:{eyebrow:"Personal Apps",title:"Apps I ship",lead:"Apps I design and publish to the App Store and Google Play on my own, outside of work.",unreleased:"Not published",highlights:"Highlights",placeholder:"Not on the store"},about:{eyebrow:"About",title:"What I can do for you"},more:{eyebrow:"More",title:"Work so far",lead:"Experience, talks and community work — open whichever you want to read."},contact:{eyebrow:"Contact",title:"Start a project",lead:"New iOS or Android apps, improving an app you already have, or just a technical question. A rough outline is enough to start.",name:"Your name",namePlaceholder:"Jane Doe",email:"Email",emailPlaceholder:"[email protected]",budget:"Budget",budgetPlaceholder:"Not decided / under $5k / $5k–15k …",message:"What would you like to build?",messagePlaceholder:"Tell me what you have in mind, where the project stands today and the timeline you are hoping for.",required:"Required",optional:"Optional",submit:"Send",submitting:"Sending…",success:"Thanks — your message is on its way.",successDetail:"I'll get back to you within two business days.",invalid:"Please check the fields above.",error:"Sending failed. Please try again in a moment.",notConfigured:"The form is not accepting messages yet. Please reach me on X or GitHub in the meantime.",rateLimited:"Too many messages in a row. Please try again a bit later.",privacy:"I use what you send only to reply to your enquiry.",honeypot:"Leave this field empty",fieldError:e=>({required:"This field is required.",invalid_email:"Please enter a valid email address.",too_long:"This is too long."})[e]},footerNote:"Always happy to talk about a mobile app."}};e.s(["default",0,i],1860);var r=e.i(91788);let n="text-sm text-muted hover:text-ink transition-colors",a=(e,t)=>"en"===e?`/en${t}`:t||"/";e.s(["default",0,({lang:e="ja",path:o="",sections:s=[],ctaHref:l,ctaLabel:c})=>{let u=i[e],p="ja"===e?"en":"ja",d=(e=>{let[t,i]=(0,r.useState)(null),n=e.join(",");return(0,r.useEffect)(()=>{if("u"<typeof IntersectionObserver)return;let e=n.split(",").map(e=>document.getElementById(e)).filter(e=>null!==e);if(0===e.length)return;let t=new Set,r=new IntersectionObserver(r=>{for(let e of r)e.isIntersecting?t.add(e.target.id):t.delete(e.target.id);let n=e.find(e=>t.has(e.id));i(n?.id??null)},{rootMargin:"-45% 0px -45% 0px"});for(let t of e)r.observe(t);return()=>r.disconnect()},[n]),t})(s.map(e=>e.id)),m=((e=12)=>{let[t,i]=(0,r.useState)(!1);return(0,r.useEffect)(()=>{let t=()=>i(window.scrollY>e);return t(),window.addEventListener("scroll",t,{passive:!0}),()=>window.removeEventListener("scroll",t)},[e]),t})();return(0,t.jsx)("header",{className:`fixed top-0 z-50 w-full transition-all duration-300 ${m?"border-b border-hairline/70 bg-white/80 py-2.5 backdrop-blur-xl":"border-b border-transparent bg-white/70 py-4 backdrop-blur-xl"}`,children:(0,t.jsxs)("div",{className:"container mx-auto flex items-center gap-4 px-6",children:[(0,t.jsx)("a",{href:a(e,""),className:"text-base font-semibold tracking-[0.18em] text-ink transition-colors hover:text-accent",children:"ENTAKU"}),(0,t.jsxs)("nav",{className:"ml-auto hidden items-center gap-7 md:flex",children:[s.map(e=>(0,t.jsxs)("a",{href:`#${e.id}`,"aria-current":d===e.id?"true":void 0,className:`relative text-sm transition-colors ${d===e.id?"font-medium text-ink":"text-muted hover:text-ink"}`,children:[e.label,(0,t.jsx)("span",{"aria-hidden":"true",className:`absolute -bottom-2 left-0 h-px bg-ink transition-all duration-300 ${d===e.id?"w-full":"w-0"}`})]},e.id)),s.length>0&&(0,t.jsx)("span",{"aria-hidden":"true",className:"h-3.5 w-px bg-hairline"}),(0,t.jsx)("a",{href:a(e,"/personal"),className:n,children:u.nav.personal}),(0,t.jsx)("a",{href:a(e,"/articles"),className:n,children:u.nav.articles}),(0,t.jsx)("a",{href:a(p,o),className:"text-sm text-accent transition-colors hover:text-accent-hover",children:u.switchLangLabel})]}),(0,t.jsxs)("div",{className:"ml-auto flex items-center gap-3 md:ml-0 md:hidden",children:[!l&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("a",{href:a(e,"/personal"),className:"text-xs text-muted transition-colors hover:text-ink",children:u.nav.personal}),(0,t.jsx)("a",{href:a(e,"/articles"),className:"text-xs text-muted transition-colors hover:text-ink",children:u.nav.articles})]}),(0,t.jsx)("a",{href:a(p,o),className:"text-xs text-accent transition-colors hover:text-accent-hover",children:u.switchLangLabel}),l&&c&&(0,t.jsx)("a",{href:l,className:"rounded-full bg-accent px-4 py-1.5 text-xs font-medium text-white transition-colors hover:bg-accent-hover",children:c})]}),l&&c&&(0,t.jsx)("a",{href:l,className:"hidden rounded-full bg-accent px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-accent-hover md:inline-block",children:c})]})})}],69711);var o=e.i(12117),s=e.i(69065);let l=o.default,c=s.default;e.s(["featuredApps",0,e=>e.personalApps.filter(e=>e.featured),"profiles",0,{ja:l,en:c}],64711)}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.