iOS/Androidã®åæéçºãé«éåããè¨è¨ææ³ã«ã¤ãã¦
æãã¾ãã¦ããã§ã¨ããããã¾ããä»å¹´ããããããé¡ãè´ãã¾ãã
å¼ç¤¾ã§ã¯ç¾å¨abceed analyticsã¨ããã¢ããªãéçºãã¦ãã¾ãããiOS/Androidã®ã¢ããªã両æ¹ä½ãéã®éçºå·¥æ°ãåæ¸ãããã¨ããã®ã¯äººé¡å ±éã®èª²é¡ãã¨æãã¾ãã
ããã§ä»åã¯ãå¼ç¤¾ãã¢ããªéçºãé«éåããããã«æ¡ç¨ãã¦ããææ³ã»æè¡ã¹ã¿ãã¯ã«ã¤ãã¦ç°¡åã«æ¸ãã¾ãã ãªããReact Native / Xamarin / Cordova ãªã©ã®ã¯ãã¹ãã©ãããã©ã¼ã ç³»ãã¬ã¼ã ã¯ã¼ã¯ã«ã¤ãã¦ã¯è§¦ãã¾ãããããã¾ã§ãã¤ãã£ãã§éçºããéã®ææ³ã§ãã®ã§ãäºæ¿ãã ããã
åæã¨ãªãèãæ¹
å¼ç¤¾ã§ã¯ãiOS/Androidã§æ°æ©è½ãåæã«ãªãªã¼ã¹ãããã¨ã¯è¡ãªã£ã¦ããã¾ãããå¤ãã®å ´åå è¡ãã¦iOSçã§æ°æ©è½ããªãªã¼ã¹ããé ãã¦Androidçããªãªã¼ã¹ãã¦ãã¾ãã åæãªãªã¼ã¹ãè¡ãªã£ã¦ããªãçç±ã¯ä¸»ã«2ã¤ããã¾ãã
1ã¤ç®ã®çç±ã¯ãæ°æ©è½ã¯ãªãªã¼ã¹å¾ã«ã¦ã¼ã¶ã¼ã®åå¿ãåæãã¦å¤æ´ãå ããããå¯è½æ§ãé«ãããã§ãã ã½ããã¦ã§ã¢ä¼æ¥ãé¥ããã¡ãªåé¡ã¨ãã¦ã5%åé¡ãã¨ãããã®ãããã¾ããããã¯ããµã¼ãã¹ãå¤æ©è½åãããã¦ä½ããµã¼ãã¹ã®æ ¸ãªã®ããè¦å¤±ã£ã¦ãã¾ããã¨ãããã®ã§ãã
ã¢ããªã«æ°æ©è½ã追å ããéã¯ãå®è£ ããåã«ãã®æ©è½ããµã¼ãã¹ã®æ ¸ãæãªããªãããååèããããã«ãã¦ãã¾ãããå®éã«ãªãªã¼ã¹ãã¦ã¿ãªãã¨åãããªãäºãããã¾ããæã«ã¯ãªãªã¼ã¹å¾ã«æ°æ©è½ãæ¤åãããã¨ãããã¾ãã®ã§ãã¾ãçæ¹ã®OSã§ãªãªã¼ã¹ãã価å¤ãé«ããã¨ãåãã£ããããçæ¹ã®OSã§ããªãªã¼ã¹ããã¨ããæ¹æ³ãè¯ãã¨æãã¦ãã¾ããâ»çµ¶å¯¾ã«å¿ è¦ãªæ©è½ã§ããäºãæ確ãªå ´åããéçºãªã½ã¼ã¹ã潤沢ãªå ´åã¯å¥ã§ã
2ã¤ç®ã®çç±ã¯ãUseCase層ããå
ã®ã³ã¼ãã¯ããªãã®é¨åãiOS/Androidéã§ä½¿ãã¾ãããããã§ãã
å¾è¿°ãã¾ãããå¼ç¤¾ã§ã¯iOS/Androidå
±ã«Clean Architecture
ã使ã£ãè¨è¨ãè¡ã£ã¦ãã¾ãããã®å ´åããã¸ãã¹ãã¸ãã¯ãViewãå¤é¨ã¤ã³ãã©ã«ä¾åããªããªãã®ã§ãã³ãã + ä¸æéãããã®æè¦ã§ä½¿ãã¾ãããããã«ãªãã¾ãã
ã»ã¼åãå 容ã®ã³ã¼ãã2人ã®äººéãå¥ã ã«ä½ãã¨ããã®ã¯å¹çãæªãã1人ãä½ã£ãå¾ã«ä½¿ãåãæ¹ãéçºå·¥æ°ã»ãã°ã®åºã«ããã®ä¸¡é¢ã§åªãã¦ããã¨æãã¦ãã¾ããããã«é¢ãã¦ã¯èãæ¹ãåããããã¨æãã¾ãã®ã§ãæ¯éãæè¦ãã ããã
è¨èª
iOSã§ã¯Swift
ããAndroidã§ã¯Kotlin
ã使ç¨ãã¦ãã¾ãã
Kotlinã使ãã¹ããã©ããã«é¢ãã¦ã¯è«¸èª¬ãããã¨ã¯æãã¾ãããã³ã¼ããiOS/Androidéã§ä½¿ãåãéã®ããããããéè¦ãã¾ãããç¹ã«Optionalãè¨èªã¬ãã«ã§ãµãã¼ãããã¦ããäºãé常ã«å¤§ããã§ãã
Swiftã§ãã使ãif let
ãNil Coalescing Operator
ãKotlinãªãç°¡åã«ç§»æ¤ããäºãã§ãã¾ããoptional chaining
ã«è³ã£ã¦ã¯å
¨ãä¸ç·ã®æ§æã§ãã
//Swift //if let if let book = bookOptional { print(book.name) } //Nil Coalescing Operator and optional chaining print(bookOptional?.name ?? "book is null")
//Kotlin //if let bookOptional?.let { print(it.name) } //Nil Coalescing Operator and optional chaining print(bookOptional?.name ?: "book is null")
ãã¨ã¯immutable
ã®å®£è¨ãval
ã¨let
ã®ã©ã¡ããã«æãã¦æ¬²ããã¨åã«é¡ã£ã¦ãã¾ãã
è¨è¨ææ³
å人ã®ããã°ã§ãä½åã触ãã¦ãã¾ãããè¨è¨ææ³ã¯iOS/Androidå
±ã«Clean Architecture
ã使ã£ã¦ãã¾ãã
詳ããå 容ã«ã¤ãã¦ã¯ããã§ã¯è§¦ãã¾ãããããã¸ãã¯ã層ãã¨ã«åãã層ã¨å±¤ã®éã®ä¾åé¢ä¿ãç¡ããäºã§ã³ã¼ãå ¨ä½ã®è¦éããè¯ããªãã¾ãã
ããã±ã¼ã¸æ§æã¯è¶
ãã£ããã ã¨ãããªæãã§ããdomainããã±ã¼ã¸ã«é¢ãã¦ã¯iOS/Androidéã§ã»ã¼ã»ã¼ä½¿ãã¾ããã¾ããé常ã®Clean Architecture
ãå°ãç°¡ç¥åãã¦ããã®ã§ã注æãã ããã
â data â â network â â repository â di â domain â â value â â entity â â model â â usecase â presentation â service â utils
UseCase層ã¯ã©ã¹ã®åãæ¹ã¯ãã¨ã³ãã£ãã£ã§ãã£ããåãã®ã¨ã¦ã¼ã¹ã±ã¼ã¹å¥ã«ç´°ããåãã®ã¨2ã¤ããã¨æãã¾ãããå¾è ã®æ¹ãè¦éããè¯ããªã£ã¦ç§»æ¤ã¯ããããæ°ããã¦ãã¾ãããã ã¯ã©ã¹æ°ãå¢ããããã®ã§ããã¯å¥½ã¿ããããã¾ããã
class UserCRUDUseCase @Inject constructor(val rep: UserRepository) { fun register() { //some process } fun update() { //some process } fun delete() { //some process } }
class RegisterUserUseCase @Inject constructor(val rep: UserRepository) { fun execute() { //some process } }
è¨è¨ææ³ã¯è²ã
ããã®ã§å¥½ããªãã®ã使ãã°è¯ãã¨æãã¾ãããè¿·ã£ã¦ãããªãGoogleãå
¬éãã¦ããAndroid Architecture Blueprints
ãåèã«ããã®ããªã¹ã¹ã¡ã§ãã
ã©ã¤ãã©ãª
iOS/Androidã§ä½¿ã£ã¦ããã©ã¤ãã©ãªã«ã¤ãã¦ã§ããã§ããã ãOSéã§ä½¿ç¨æãå¤ãããªããããªãã®ãé¸ãã§ãã¾ãã
API
iOSã§ã¯APIKit + ObjectMapperããAndroidã§ã¯retrofit2 + Moshiã使ã£ã¦ãã¾ãã
APIã®ã¢ã¯ã»ã¹æ
å ±ã¯data/network
ã«æ ¼ç´ãã¾ããAPIã®ã¬ã¹ãã³ã¹ã«é¢ãã¦ã¯domain/entity
ã«æ ¼ç´ãã¦ãã¾ãããdata
層ã«æ ¼ç´ãã¦å¤æãdomain
層ã§åã¾ããæ¹ãè¯ãã¨æãæãããã¾ãã
iOS
class UserInfoRequest: BaseAPIRequestType { var path: String { return "user/info" } var method: HTTPMethod { return HTTPMethod.get } //Response typealias Response = UserInfoResponse var id_user:String init(id_user:String) { self.id_user = id_user } //Request Parameters func toDict() -> Dictionary<String, Any> { var dict = Dictionary<String, Any>() dict["id_user"] = id_user return dict } } class UserInfoResponse: Mappable { var id_user:String? var name_user:String? required init?(map: Map) { } func mapping(map: Map) { id_user <- map["id_user"] name_user <- map["name_user"] } }
Android
interface UserApi { @GET("user/info") fun info(@Query("id_user") id_user:String): Call<UserInfoResponse> } data class UserInfoResponse(val id_user:String, val name_user:String)
å é¨DB
å é¨DBã¯iOS/Androidå ±ã«Realmã使ã£ã¦ãã¾ããã·ã³ãã«ã§ä½¿ããããã§ãããã¹ã¬ãããã¾ããæã«æ³¨æãå¿ è¦ã§ãã Androidã§ã¯UseCase層ãå¥ã¹ã¬ããã§å®è¡ããäºãå¤ãã®ã§ãé½åº¦Viewç¨ã®ã¢ãã«ã«å¤æãã¦ãã¾ããããã«é¢ãã¦è¯ãæ¹æ³ããã°æ¯éæãã¦ãã ããã
DI
Androidã§ã¯Dagger2ãå°å ¥ãã¦ãã¾ããAndroidã¯Contextãå¿ è¦ãªç®æãå¤ãã®ã§ãããContextãå¼ãåãã¦ããã¨ã³ã¼ãã®è¦éããæªããªãã®ã§DIãå°å ¥ããå©ç¹ã大ããæ°ããã¦ãã¾ãã
DIã使ãã¨ãä¾ãã°SharedPreferenceã以ä¸ã®ããã«Repository層ã«æ¸¡ããã®ã§ãå人çã«æ¸ããããã§ãã
@Module class AppModule { @Provides @Singleton fun provideContext(application: Application): Context { return application } @Provides @Singleton @Named("default") fun provideDefaultSharedPreference(context: Context): SharedPreferences { return PreferenceManager.getDefaultSharedPreferences(context) } } @Module internal class RepositoryModule { @Provides @Singleton fun provideUserRepository(@Named("default") ref: SharedPreferences): UserRepository { return UserRepository(ref) } } class UserRepository @Inject constructor(val ref: SharedPreferences) : UserDataSource { }
iOSã«é¢ãã¦ã¯ãè¯ãã©ã¤ãã©ãªãè¦ã¤ãããã¦ããªããã¨ãããæåã§ã®DIã¨ãªã£ã¦ãã¾ã£ã¦ãã¾ããè¯ãæ¹æ³ããã¾ããã誰ãæãã¦ãã ããã
ã¾ã¨ã
å¼ç¤¾ã§ã¯ã
ã¾ãçæ¹ã®OSã§ãªãªã¼ã¹ãã価å¤ãé«ããã¨ãåãã£ããããçæ¹ã®OSã§ããªãªã¼ã¹ãã
Kotlinãæ¡ç¨ããSwiftããã®ç§»æ¤ã³ã¹ããä¸ãã
Clean Architectureãæ¡ç¨ãã¦ãã¸ãã¹ãã¸ãã¯ãç°¡åã«iOS/Androidéã§ä½¿ãåããããã«ãã
Androidã§Dagger2ãæ¡ç¨ããRepository層ã®è¨è¨ãiOS/Androidã§åãã«ãªãããã«ãã
ã¨ãã£ãæ¹æ³ã§éçºã®é«éåãå³ã£ã¦ãã¾ãã ã¾ã ã¾ã æãã¨ãããå¤ãããã¾ãã®ã§ãããæ¬è¨äºã«ãæè¦ãªã©ããã¾ãããæãã¦ããã ããã°å¹¸ãã§ãï¼
ãªããå¼ç¤¾ã§ã¯ç¾å¨ã¨ã³ã¸ãã¢ãåéä¸ã§ãããã®è¨äºãèªãã§ããèå³ããããã§ãããæ¯éãªãã£ã¹ã«éã³ã«æ¥ã¦ä¸ããï¼ ã¢ããªã¨ã³ã¸ãã¢ä»¥å¤ã«ãWebã¨ã³ã¸ãã¢ããã¼ã¿åæå±ãããæè¿ãã¦ãã¾ãã