> 2021年08月19日信息消化 ### 実践 Android / iOSアプリ設計 - iOS - Xcode 9.4 - Swift 4.1 - iOS 11.0~11.4 - Android - Android Studio 3.1~ - Kotlin 1.2 #### DI パターンで疎結合なコード 依存関係の解決:依存を外から渡すデザインパターン ViewControllerの依存関係の解決 -- DIパターンの活用 ```swift class someAPIClinet{ func request() { //...} } class ViewController: UIViewController { private var apiClient = SomeAPIClient() func request(){ apiClient.request() } } ``` ↑のコードにはいくつか課題があります。 1)APIクライアントが特定の型に固定されている。 2)API クライアントを外部から変更する方法がない 3)型の依存関係が不明瞭 まずは依存関係を肩から判別可能にするためInjectableプロトコルを用意します。 ```swift protocol Injectable { associatedtype Dependency init(with dependency: Dependency) } ``` 依存関係の定義 ```swift class ViewController: UIViewController, Injectable { // SomeAPIClientを依存オブジェクトに typealias Dependency = someAPIClient private let apiClient: SomeAPIClient required init(with dependency: Dependency) { apiClient = dependency super.init(nibName: nil, bundle:nil) } // ... } // ViewControllerクラスのインスタンス化 let vc = ViewController(with: SomeAPIClient) ``` #### APIクライアントの抽象化ーープロトコルの活用 解決:APIクライアントが特定の型に固定されている。 ```swift protocol APIRequest { associatetype Response } protocol APIClient { func reponse( from request: R, completion: ((R.Response) -> ())? ) } ``` テスト ```swift // APIRequestに適合したモックオブジェクト struct MockRequest: APIRequest { typealias Response = [String] } //モックAPIクライアント class MockAPIClient: APIClient { func reponse( from request: R, completion: ((R.Response) -> ())?) { completion?(["test"] as! R.Response) } } class ViewController: UIViewController, Injectable { // ... // reponseを格納するプロパティ var items: [String] = [] func request() { let req = MockRequest() apiClient .reponse(from: req) { [weak self] reponse in // reponseを代入 self?.items = reponse } } } ``` ##### Android APIクライアントの依存関係の解決 -- Daggerの活用 まず、Applicationクラスを継承したクラスのプロパティとして自動生成されるDaggerAppComponentクラウsのオブジェクトをプロパティとして定義します。 ```kotlin // ShoppingApp.kt class ShoppingApp: Application() { val component: AppComponent = DaggerAppComponent.creat() } ``` 次に@Scopeを用意します。@ScopeはDIパターンを適用する範囲を決めるためのアノテーションです。 ```kotlin // ActivityScope.kt @Scope @Retention(AnnotationRetention.RUNTIME) annotation class ActivityScope ``` #### MVVM パターンでUIとロジックの分離 iOS/Androidアプリ開発の中心的なコンポーネントとして、iOSではViewController、AndroidではActivityがあり、画面の生成から消滅までのライフサイクルの管理を担う。 MVCModel View Controller MVVM - Model: ビジネスロジック - View:画面の描写と入力の受付 - ViewModel:プレゼンテーションロジック ##### Reactive Extensions Swift ライブラリ管理ツール:CocoaPods、Carthage RxSwiftをCarthageでインストール ```bash brew install carthage vi Catfile # github "ReactiveX/RxSwift" ~>4.0 carthage update --platform ios ``` ViewModelの実装 PublishSubjectはイベントストリームの管理を行うRxの主要なクラス。入力:Observer 出力: Observable BehaviorSubjectもRxのクラスです、リストデータに使うことで、最新の商品リストデータを保持できます。 ```swift // ItemListViewModel.swiftprivate let viewWillAppearStream: PublishSubject<()>private let cartButtonDidTapStream: PublishSubject<()>// ViewにはObserverのみ公開するvar viewWillAppear: AnyObeserver<()> { return viewWillAppearStream.asObserver()}//商品リストデータprivate let itemsStream: BehaviorSubject() //ViewにはObservableのみ公開var items: Observable<[Item]> { return itemsStream.asObservable()}// inputとoutputのバインディング// データは画面が表示されるタイミングで、APIクライアントに商品リストのリクエスト// 取得したデータを、商品リストのイベントストリームに接続private let disposeBag = DisposeBag()init(with dependency: Dependency) { let apiClient = dependency.apiClient viewWillAppearStream .flatMapLatest { _ -> Observable<[item]> in let request = API.ItemListRequest() return apiClient.reponse(from: request) } .bind(to: itemsStream) .disposed(by: disposeBag)} ``` disposed(by:) メソッドは、イベントストとリームを破棄するために使用:オブジェクトの解放。 ##### Viewの実装 Itemの配列を引数で受け取り画面を更新するロジックを、ViewControllerに実装。 データを取得した後:UITableViewのreloadDataメソッドを実行。 rx.sentMessageで任意のセレクタをイベントストリームに変換 ```swift // ItemListViewController.swiftprivate func reloadData(_ data: [item]) { dataSource = data tableView.reloadData()}// データバインディングprivate func bind() { rx.sentMessage(#selector(viewWillAppear(_:))) .map { _in } .bind(to: viewModel.viewWillAppear) .disposed(by: disposeBag)}viewModel.items .bind { [weak selft] items in self?.reloadData(items) } .disposed(by: disposeBag) ``` MVCでがViewControllerにあるプレゼンテーションロジックをViewModelに実装することで、ViewControllerno責務を減らすとともに、仕様追加コード変更時の影響範囲がコンポーネント内に留まりやすくなりました。 ##### Android MVVMの実践 非同期処理のために、Rxの代表的なライブラリであるRxJava、RxAndroid、RxKotlinを導入 1. build.gradleのdependenciesブロックに、ライブラリの依存を記述 2. Databindingライブラリの導入 ```gradle implementation "io.reactivex.rxjava2:rxjava:2.1.13"implementation "io.reactivex.rxjava2:rxandroid:2.0.2"implementation "io.reactivex.rxjava2:rxkotlin:2.2.0"android { dataBinding { enabled = true }} ``` ### NPM Dependency errors? Then You’re doing it wrong. origin: [NPM Dependency errors? Then You’re doing it wrong.](https://medium.com/netscape/npm-dependency-errors-then-youre-doing-it-wrong-635160a89150) ##### The Problem with non-deterministic dependencies You set up a new **Node JS/Webpack project**, installed all your dependencies with *npm install* and your app runs smoothly. A week later, another developer has been assigned to work along with you. So he/she cloned it and install dependencies via *npm install*, then they run the app and all of sudden, errors everywhere! The answer is **bad dependency tree management**. This is a common problem within the **NPM ecosystem** which every developer faces. - **PATCH:** Bug fixes and other minor changes: Patch release, increment the last number, e.g. 0.0.1 - **MINOR:** New features which don’t break existing features: Minor release, increment the middle number, e.g. 0.1.0 - **MAJOR:** Changes which break backwards compatibility: Major release, increment the first number, e.g. 1.0.0 We know that SemVer’s prefix **^(caret)** symbol will update you to the most recent minor version or patch level. Consider **JQuery** library ```json "jquery": "^3.0.1" ``` - **DO NOT DELETE** *package-lock.json* or *npm-shrinkwrap.json* file. - You **SHOULD** commit your package-lock to VCS. - Keep your module up to date using the command `npm outdated` **Tips #3** Use [**npmvet**](https://www.npmjs.com/package/npmvet) **.** Really**,** it’s very useful to manage the npm packages versions which are installed locally. ![img](https://raw.githubusercontent.com/Phalacrocorax/memo-image-host/master/uPic/1*EShf63vwMNboDZixXSRTcg.png) ### The rise of the one-person unicorn origin: [The rise of the one-person unicorn](https://www.nothingventured.com/the-rise-of-the-one-person-unicorn/) The way we build startupsinicios is changing, and with that change comes the rise of a of company: bootstrapped, cash generative, and growing like crazy. ##### The way we build startups is changing. Software didn’t just eat the world; it spewed out a new one. The internet enabled us to build an intangible global economy, and software is both the mechanism, and much of the output. Those building know it’s becoming easier to do so. An AWS economy, barriers to entry falling away on a near-daily basis and the potential for infinite distribution. It might be the make-up of the intangible economy, but building software can be highly tangible: pattern-recognition, predictable growth levers, to constantly foreground an up-and-to-the-right journey of growth and profitability. The underbelly of building is, and always has been, funding. That’s changing too – and in concert with changes in the market. It’s these shifting sands that I’m concerned with in this post. We’ve touched on it being cheaper and easier to build software companies. Understanding this in combination with funding requires us to think about what can be built at this stage of the market. ##### Hyper-specific, profitable, company-building The answer is narrow, vertical businesses designed to solve single, specific problems. Companies like Headlime, which helps writers generate better headlines, ConvertKit, email marketing software for creators, Fathom, who are building a privacy-focused alternative to Google Analytics, and Transistor, a podcast hosting and distribution platform. ##### The VC’s lunch To date, the standard VC routine has covered off both growing your business, and exiting it. Outside investors join the board and cap table and keep the pressure on, often from pre-product to exit. That’s fine, and it’s an incredible model which has underpinned software for well over three decades. For a founder, it means a decade plus of intensely hard work, followed by – if you stay alive – walking away with 5 or 10 percent of your business. It’ll likely be no small sum (you’ll make between $50m to $100m on a “unicorn”) – the dilution is likely worth the effort. But now, we’ve got an alternative route. Founders – often serial – know the SaaS/startup playbook and it takes them a matter of months to take a deliberately small, self-funded startup from inception to tens, if not hundreds of thousands a month in revenues. They’re answerable to no-one but their customers, themselves (if they choose to keep running it), and can sell it for a healthy multiple of revenue. > HN Comment @nickjj > > One thing this article doesn't pointpoint out is how beneficial it is to have a decently`decente` **large highly engaged audience beforehand**.ConvertKit is an example of this.The founder started by selling books and then made a course on how to sell books, got a pretty big audience around that and then started to build ConvertKit. Eventually he figured out who he wanted to market ConvertKit to and his prior audience of people wanting to sell things online was a great match. Naturally a lot of people who bought his books and courses are a great fit for his email service because the whole premise of his course on selling books is around building an email list. The books ended up being a lead generator for his SAAS app.That's not to take anything away from Nathan's work. At the end of the day he made a very successful and well deserved business but the specific circumstances around it are a bit different than what this blog post makes it out to be. It was a 10 year journey with multiple good ideas at the right time to get to that point. All of it is documented on Nathan's site. I remember reading some of his posts many years ago. > > HN Comment @4thethrilllofit > > To all the people saying it’s impossible — I’ve been bootstrapping an analytics company in ecomm space. From launch to 500k ARR took 9 months with almost entirely inbound. (Aka 0 marketing cost) I didn’t have an audience prior or spent any time at all on sites like Reddit. It’s not easy to do by any means, but here’s what I believe are the requirements: > > \- a **true generalist**: technical, product, sales, design. > > \- **deep experience** in the industryla industria you are gonna serve. Hanging out on Reddit does NOT get you this. You have to have worked in the industry prior on high levelnivel roles. It’s what’ll give you credibility to potential buyers. > > \- design a `producta` product and business model`modelo` that’s niche and painful enough for a large AND expanding TAM. > > \- there’s has to be sufficient dissatisfaction with incumbents. Customers are craving for better solutions and motivated to find one. > > \- you can **effectively source** and work with contractors to fill the gaps in your skills and time. > > \- the product must have **near zero churn**. Otherwise the leaky bucket is too much to fill without an actual sales force. This is possible, my business is exactly this. > > \- you know how to be capital efficient and can product low cash outflow consistently. > > \- **you are not personally in need of an actual income for at least 3 years. Preferably more.** > > I believe the above are a must for a single bootstrapped founder to create a high growth startup. The optionality it affords you is tremendous and you will stand above the rest. ### 一点收获 - データベーススキーマの管理 - Ridgepoleによるスキーマ定義のコード化 - DSL