2019년 10월 17일 목요일

Convenient and idiomatic conversions in Rust 를 읽고

https://ricardomartins.cc/2016/08/03/convenient_and_idiomatic_conversions_in_rust

Rust에서 타입을 conversion하는데에 사용되는 trait가 있습니다.

From<T>, Into<U>, TryFrom<T>, TryInto<U>, AsRef<U>, AsMut<U>

=> T가 원본 타입이고 U가 타겟이 되는 타입이라고 보면 된다.

From<T> for U

T라는 타입을 U으로 변경하는 trait
reflexive trait임. From<T> for T 의 경우 자기자신을 그대로 리턴한다.
실패하지 않는다.
From<T> for U를 구현하면 Into<U> for T도 같이 자동으로 구현된다.

Into<U> for T

T라는 타입을 U로 변경하는 trait

TryFrom<T>

From<T>와 같은데 변환에 에러가 있을 수 있는 경우 사용한다. 따라서, 결과가 Result<Self, Self::Error>이다.

TryInto<U>

TryFrom<T>와 같다고 보면 된다. 결과가 Result<T, Self::Error> 이다.

AsRef<U>

immutable reference를 다른 타입의 immutable reference로 변환한다.

AsMut<U>

mutable reference를 다른 타입의 mutable reference로 변환한다.

AsRef<U>와 AsMut<U>에는 generic implementation이 있다.

=> AsRef<U>나 AsMut<U>를 구현하고 있는 type에의 reference에 대한 구현으로서
&&&&vec이나 &&&& mut vec 같은 multiple-level deep reference를 &vec와 같게 만들어 준다.(왜 이렇게 되는건지 어떤 사용 예가 있는 건지 이해 못함ㅠㅠ)

2019년 10월 16일 수요일

Testing two consecutive LiveData emissions in Coroutines 를 읽고

https://medium.com/androiddevelopers/testing-two-consecutive-livedata-emissions-in-coroutines-5680b693cbf8

LiveData에서의 데이타 발생을 테스트할 때 발생했던 문제를 이야기 합니다.

예를 들어 네트워크로부터 데이타를 가져오는데 시간이 걸리는 경우 우선은 화면에 더미? 또는 간단한 데이타를 보여주고 실제 데이타를 얻어오면 다시 화면을 업데이트 하게 할 수 있습니다.
이에 대한 테스트 코드를 만들어서 테스트를 한다면 하나의 LiveData를 만들어서 emit이 두번 일어나는지를 확인해 보게 될겁니다. 그런데 LiveData는 최종 데이타만을 가지고 있기 때문에 Dispatcher.UnConfined의 코루틴으로 테스트를 했더니 항상 첫번째 emit은 일어나지 않고 두번째 emit만 일어나서 테스트가 실패했다고 합니다.

이에 대한 해결책으로 TestCoroutineDispatcher의 pauseDispatcher와 resumeDispatcher를 사용해서 두번 emit이 일어날 수 있도록 변경했다고 합니다. TestCoroutineDispatcher에 코루틴의 동작을 제어(중단 및 다시 실행)할 수 있는 함수가 있어서 이를 통해 해결이 가능했네요.

이외에 liveData coroutines builder를 사용해서도 해결할 수 있다고 합니다. 이 builder를 쓰면 데이타를 전달할 때 emit() 함수를 사용하게 되는데 이 경우 데이타 전달이 확실히 일어나게 되는거 같네요.

Good Practices

- 코루틴을 사용할 때 Dispatcher는 꼭 Dependency Injection의 형태로 사용해라.

- Dispatchers.UnConfined 대신에 TestCoroutineDispatcher를 사용해라.


ViewModel with SavedStateHandle

ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loaders

Saving UI state with ViewModel SavedState and Dagger

ViewModels: State persistence — SavedState

A Deep Dive into Extensible State Saving

요즘 안드로이드 개발에서는 ViewModel을 많이 사용합니다. Configuration 변경에 대한 처리를 위해 사용하기도 하겠지만 저같은 경우는 MVVM 아키텍쳐 적용에 따라 ViewModel을 사용하고 있습니다.
안드로이드에서는 보통은 view에 관련된 data를 저장하고 싶을 때 onSaveInstanceState를 통해 저장합니다. 그리고 나서 나중에 Activity나 Fragment가 생성될 때 savedInstanceState를 확인해서 필요한 값이 있으면 꺼내서 사용하죠.
그런데 MVVM을 사용하게 되면서 data를 ViewModel쪽으로 이동하게 되고 이럴경우 data를 어떻게 유지할 것인가가 이슈가 됩니다.
이에따라 구글에서는 이에 관련된 lifecycle-viewmodel-savedstate 라는 모듈을 내놓았습니다. 이 모듈에서는 어떻게 ViewModel과 Activity/Fragment 사이에서 state(data)를 유지하는지 살펴보겠습니다.

크게는 Activity/Fragment에서의 처리와 ViewModel에서의 처리 이렇게 둘로 나누어서 볼 수 있습니다.

Activity/Fragment의 경우는 기본 컨셉은 간단합니다. 종료시 ViewModel에서 제공하는 state를 저장하고 다시 시작할 때 이렇게 저장된 state를 꺼내와서 ViewModel에 전달해 줍니다.

Activity/Fragment -> SavedStateRegistryController -> SavedStateRegistry

위의 관계로 SavedStateRegistry의 performSave/performRestore 함수에서 state를 저장하고 꺼내오는 역할을 해줍니다.(코드로 보면 onCreate에서 performRestore를 호출하고, onSavedInstanceState에서 performSave를 호출해 줍니다.)
또한 Activity/Fragment는 SavedStateRegistryOwner를 구현합니다. 이때의 구현 함수인 getSavedStateRegistry()를 통해 Activity/Fragment로부터 SavedStateRegistry를 얻어서 사용할 수 있습니다.(나중에 ViewModelFactory쪽에서 사용하게 됩니다.)

ViewModel을 생성하기 위해 SavedStateViewModelFactory를 사용합니다. 아래처럼 사용하는 건 다들 아실겁니다.

private val viewModel by viewModels { SavedStateViewModelFactory(SavedStateRegistryOwner) }

짜잔~ Factory에서 SavedStateRegistryOwner를 파라메터로 받네요. Factory는 SavedStateRegistryOwner로부터 SavedStateRegistry를 얻어오고 이것으로 부터 자신이 저장했던 state를 가져와서 ViewModel에 다시 파라메터로 넘겨줍니다.

간단하게 코드를 살펴보려면 AbstractSavedStateVMFactory의 create 함수를 보면 됩니다.
SavedStateRegistry의 consumeRestoredStateForKey를 호출해서 state를 얻고, 또한 ViewModel에서 저장한 state가 저장될 수 있도록하기 위해 SavedStateRegistry의 registerSavedStateProvider를 호출해서 지금 생성하는 ViewModel의 key와 SavedStateProvider를 등록해 줍니다.

앞에서 state를 저장할 때 performSave를 호출한다고 했습니다. 이때 저장된 모든 (key, provider)에서 provider를 꺼내와서 provider.savedState()를 통해 state를 저장합니다. ViewModel에서 저장하고자 하는 state는 SavedStateHandle에 저장되게 되는데요. SavedStateProvider의 savedSate()를 호출하면 SavedStateHandle에 저장된 state를 가져오게 됩니다.

ViewModel -> SavedStateHandle -> SavedStateProvider

2019년 10월 12일 토요일

libp2p: Transport 를 읽고

https://docs.libp2p.io/concepts/transport/

libp2p는 p2p 관련 프로토콜의 집합입니다.
p2p 기능을 원하는 다른 곳에서 쓰일 수 있도록 라이브러리로 되어 있습니다.

libp2p가 동작하기 위한 기본이 되는 네트워크 연결은 다양한 프로토콜을 통해 이루어 질 수 있게 되어 있습니다.(libp2p에서는 이를 transport라 부릅니다.)
이를 위해 libp2p는 TCP, WebSocket, WebRTC, QUIC등 다양한 프로토콜(참고)을 지원하고 있습니다.

개별 peer간에 연결이 이루어지려면 peer를 구별하는 이름같은 것이 있어야 할 텐데요. 이를 위해 multiaddress(참고)를 사용합니다.
간단하게 예를 들어보면 다음과 같은 형태가 됩니다.

/ip4/1.2.3.4/tcp/4321/p2p/QmcEPrat8ShnCph8WjkREzt5CPXF2RwhYxYBALDcLC1iV6

ip4 주소는 1.2.3.4 이고, tcp 포트는 4321이고, QmcEPrat...는 PeerId를 의미하는 데요. 누구에게 접속하는지를 표시하는 거라고 보면 됩니다.

PeerId(참고)를 좀 더 자세히 볼까요? peer를 유일하게 구별하는 이름인데요.
기술적으로는 public key의 해시 값입니다. 개별 peer들은 동작시 private/public key 쌍을 생성해서 유일한 값을 만들어 내고 이를 자기를 구별하는 값으로 사용합니다.

libp2p를 사용하는 애플리케이션이 하나의 transport만을 사용할 필요는 없습니다. 동시에 여러개의 transport를 지원할 수 있습니다.
이를 switch라고 합니다. switch를 통해 protocol negotiation, stream multiplexing, secure communications, connection upgrading을 할 수 있습니다.

2019년 10월 11일 금요일

ViewBinding

layout으로부터 view를 가져오는 가장 원초적인 방법은 findViewById를 사용하는 겁니다.
그런데 이건 워낙에 코드를 장황하게 만들어서 귀찮죠.

그래서 보통 간단하게 사용할 수 있는 Buffer Knife나 Kotlin Android Extensions를 사용합니다.

저도 이전 앱에서는 Buffer Knife를 사용했었고 현재 앱에서는 Kotlin Android Extensions를 사용하고 있습니다.

하지만 구글에서 ViewBinding을 새로 내놨으니 다음에는 ViewBinding을 써야겠네요.

https://proandroiddev.com/hello-viewbinding-goodbye-findviewbyid-edca92b397c

2019년 1월 23일 수요일

Swift By Sundell - Basics

https://www.swiftbysundell.com/basics

- Unit Testing

Xcode에 포함되어 있는 XCTest를 사용한다.

import XCTestCase

class는 XCTestCase를 상혹하고 함수 이름을 test로 시작하게 짓는다.

함수의 내용은 Given - When - Then 의 형태로 구현한다.
"Given these conditions when these actions are performed, then this is the expected outcome"

Given이 중복되는 경우 setUp() 함수를 사용한다.
setUp: 테스트 실행시 항상 먼저 실행되는 함수

- Grand Central Dispatch

GCD: "a different queue of execution"을 통해 asynchronous하게 code가 실행되도록 한다.
DispatchQueue.main
DispatchQueue.global

qos, attributes 등의 속성을 정해줄 수 있다.

- Layout Anchors

Auto layout을 편하게 지정하게 해준다. iOS9에서 새로 소개되었다.

UIView: "a series of anchors"를 포함하고 있다.

anchor를 지정하고 나면 항상 활성화를 해주어야 한다. 다음의 두가지중 한가지 방법을 사용한다.

1. constraint.isActive를 true로 설정
2. NSLayoutConstraint.activate([constraint])

고려해 봐야 할 점 세가지

1. 기본적으로 모든 view는 initial auto resizing mask를 layout constraints로 바꾼다. 이걸 disable하고 싶으면 setTranslatesAutoresizingMaskIntoConstraints를 false로 설정한다.
2. 여러 view로부터의 anchor를 사용하려면 모두가 같은 view hierarchy에 속해 있어야 한다.
3. constraint can be dynamically enabled and disabled. 이러한 경우 constraint를 add/remove 하기 보다는 isActive를 사용하는 것이 낫다.

- Child View Controllers

1. Parent에 add하기

parent.view.addSubView
parent.addChild
child.didMode

2. Parent에서 remove 하기

child.willMode
child.removeFromParent
child.view.removeFromSuperview

=> 항상 3개의 함수를 호출해 주어야 하므로 extension으로 add/remove 함수를 만들어서 사용하면 편할 것이다.

3. UI를 만들 때 Child view controller로 만들면 유용한 점이 있다.

- viewDidLoad나 viewWillAppear 같은 event를 받아서 처리할 수 있다.
- UI와 UI에 관련된 로직을 한군데에 포함함으로서 하나의 단위로 사용될 수 있다.
- View controller는 child로 추가되면 화면 전체를 차지하게 된다. 따라서, 별도로 full screen UI를 위한 layout code를 만들지 않아도 된다.
- 여러곳에서 사용될 수 있게 된다. Navigation Controller에 push 된다던가, child로 embedd 된다던가.

- Codable

Codable은 단순히 Encodable과 Decodable의 type alias이다.

typealias Codable = Decodable & Encodable

예를 들어 struct User가 Codable를 상속받으면 JSONEncode를 사용해서 쉽게 JSON으로 encoding할 수 있다.

struct User: Codable { ... }

let data = try JSONEncoder().encode(user)
let data2 = try JSONDecoder().decode(User.self, from: data)

그런데 json 포맷이 안맞으면 어떻게 해야 할까?
- 변수 이름을 포맷에 맞출까?

json 포맷에 맞는 struct를 만들어 decoding을 한 후 다시 내가 사용하는 struct로 변환한다.

snake case와 camel case가 안맞는 경우 keyDecodingStrategy를 지정해 주면 된다.

decoder.keyDecodingStrategy = .convertToSnakeCase


2018년 6월 10일 일요일

안드로이드 아키텍쳐 패턴을 알아보자

- Clean architecture에 대한 설명

Architecting Android...Reloaded

위 블로그 내용에 대한 Github 소스

https://github.com/android10/Android-CleanArchitecture-Kotlin

- Clean architecture boilerplate using the Model-View-Intent pattern

https://github.com/bufferapp/android-clean-architecture-mvi-boilerplate

- Redux 스타일로 구현하는 방식에 대한 설명. 총 8편의 씨리즈로 되어 있다.

Reactive apps with MVI

- Coordinator Pattern에 대해 알아본다.

In-app navigation with coordinators

위 블로그 내용에 대한 Github 소스

https://github.com/sockeqwe/CoordinatorsAndroid

- 많이 이야기되는 패턴에 대한 장단점 나열

결론으로 Redux 쓰지말고 MVVM 쓰라고 함

MVC/MVP/MVVM/CLEAN/VIPER/REDUX/MVI/PRNSAASPFRUICC

Generic interfaces 요점

 https://go.dev/blog/generic-interfaces  Generic interface를 정의할 때 최소한의 제약만을 정의하고 실제 구현체들이 자신만의 필요한 제약을 추가할 수 있도록 하는 것이 좋다. pointer receiver를...