SlideShare uma empresa Scribd logo
1 de 85
Baixar para ler offline
Unit Testing
Zašto?
Kako ubiti

2 aplikacije
Jednim commitom.
Manje bugova.
Swift 

migracija?
Nema problema.
Manje stresa.
Kad se isplati?
Testiranje je
investicija
Što testirati?
Koliko se koristi?

Koliko je kompleksno?

Koliko je važna korektnost?
Što testirati?
1. Core logika

2. Prezentacija (ViewModel)
3. UI
Zašto ljudi ne

testiraju?
Testovi su

teški.
Ako je dizajn loš.
Teško za
testirati:
Dependency (UIKit,
Foundation, libovi)
Lagano za

testirati:
“Čisti” Swift
Business logika
Granice
Razdvojiti Swift od plaforme i
libraryja
Izdvojiti logiku aplikacije
MVVM
UIViewController
NAVIGACIJA
UIKIT
PRIKAZ
UI LOGIKA
NETWORKING
BUSINESS

LOGIKA
TRANSFORMA
CIJA
PODATAKA
MIJENJANJE

STANJA
UIViewController
NAVIGACIJA
UIKIT
PRIKAZ
UI LOGIKA
NETWORKINGBUSINESS

LOGIKA
TRANSFORMA
CIJA
PODATAKA
MIJENJANJE

STANJA
ViewModel
MVVM++
Example
States
LoadedLoading No data Error
States
class FeedViewModel {
enum ViewState {
case loading
case noData
case error(String)
case loaded([FeedCellModel])
}
}
Data
struct FeedCellModel {
let name: String
let description: String
let image: URL?
}
Lagano za verificirati,
bez stanja (nepromjenljivo)
class FeedViewModel {
func loadData() {
self.state = .loading
feedService.getFeedItem(page: 0) { result in
switch result {
case let .success(items):
if items.isEmpty {
self.state = .noData
} else {
let models = self.feedCellModels(from: items)
self.state = .loaded(models)
}
case let .failure(error):
let errorText = self.errorText(for: error)
self.state = .error(errorText)
}
}
}
}
class FeedViewModel {
func loadData() {
self.state = .loading
feedService.getFeedItem(page: 0) { result in
switch result {
case let .success(items):
if items.isEmpty {
self.state = .noData
} else {
let models = self.feedCellModels(from: items)
self.state = .loaded(models)
}
case let .failure(error):
let errorText = self.errorText(for: error)
self.state = .error(errorText)
}
}
}
}
class FeedViewModel {
func loadData() {
self.state = .loading
feedService.getFeedItem(page: 0) { result in
switch result {
case let .success(items):
if items.isEmpty {
self.state = .noData
} else {
let models = self.feedCellModels(from: items)
self.state = .loaded(models)
}
case let .failure(error):
let errorText = self.errorText(for: error)
self.state = .error(errorText)
}
}
}
}
class FeedViewModel {
func loadData() {
self.state = .loading
feedService.getFeedItem(page: 0) { result in
switch result {
case let .success(items):
if items.isEmpty {
self.state = .noData
} else {
let models = self.feedCellModels(from: items)
self.state = .loaded(models)
}
case let .failure(error):
let errorText = self.errorText(for: error)
self.state = .error(errorText)
}
}
}
}
class FeedViewModel {
func loadData() {
self.state = .loading
feedService.getFeedItem(page: 0) { result in
switch result {
case let .success(items):
if items.isEmpty {
self.state = .noData
} else {
let models = self.feedCellModels(from: items)
self.state = .loaded(models)
}
case let .failure(error):
let errorText = self.errorText(for: error)
self.state = .error(errorText)
}
}
}
}
class FeedViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
viewModel.onStateChanged = { [weak self] state in
guard let `self` = self else { return }
switch state {
case .noData:
self.refreshControl.endRefreshing()
self.manager.data = []
self.errorImage.isHidden = true
self.errorLabel.text = Localizable.error_empty_feed
self.errorView.isHidden = false
case .error(let error):
self.manager.data = []
self.refreshControl.endRefreshing()
self.errorImage.isHidden = false
self.errorLabel.text = error
self.errorView.isHidden = false
case .loaded(let items):
self.refreshControl.endRefreshing()
self.manager.data = items
self.errorView.isHidden = true
case .loading:
self.refreshControl.beginRefreshing()
}
}
}
Prednosti
1. Cijeli state na jednom mjestu
2. Manipulacija statea izdvojena iz iOS-a
3. Lagano ubacivanje dodatnog 

statea
Prednosti
Testabilan dizajn = dobar dizajn
Testiranje
1. Spremi promjenu statea u array.
2. Okini akcije.

3. Provjeri state array.
class FeedDetailsViewModelTests: XCTestCase {
var feedService: FeedServiceMock!
var states: [FeedDetailsViewModel.ViewState] = []
var viewModel: FeedDetailsViewModel!
override func setUp() {
super.setUp()
feedService = FeedServiceMock()
states = []
viewModel = FeedViewModel(feedService)
viewModel.onState = { self.states.append($0) }
}
class FeedDetailsViewModelTests: XCTestCase {
var feedService: FeedServiceMock!
var states: [FeedDetailsViewModel.ViewState] = []
var viewModel: FeedDetailsViewModel!
override func setUp() {
super.setUp()
feedService = FeedServiceMock()
states = []
viewModel = FeedViewModel(feedService)
viewModel.onState = { self.states.append($0) }
}
class FeedDetailsViewModelTests: XCTestCase {
var feedService: FeedServiceMock!
var states: [FeedDetailsViewModel.ViewState] = []
var viewModel: FeedDetailsViewModel!
override func setUp() {
super.setUp()
feedService = FeedServiceMock()
states = []
viewModel = FeedViewModel(feedService)
viewModel.onState = { self.states.append($0) }
}
class FeedDetailsViewModelTests: XCTestCase {
var feedService: FeedServiceMock!
var states: [FeedDetailsViewModel.ViewState] = []
var viewModel: FeedDetailsViewModel!
override func setUp() {
super.setUp()
feedService = FeedServiceMock()
states = []
viewModel = FeedViewModel(feedService)
viewModel.onState = { self.states.append($0) }
}
final class FeedDetailsViewModelTests: XCTestCase {
// …

func testLoadDataTransitionsToLoadedState() {
viewModel.loadData()
switch (states.first!, states.last!) {
case (.loading, .loaded):
break
default:
XCTFail()
}
}
}
final class FeedDetailsViewModelTests: XCTestCase {
// …

func testLoadDataTransitionsToLoadedState() {
viewModel.loadData()
switch (states.first!, states.last!) {
case (.loading, .loaded):
break
default:
XCTFail()
}
}
}
final class FeedDetailsViewModelTests: XCTestCase {
// …

func testLoadDataTransitionsToLoadedState() {
viewModel.loadData()
switch (states.first!, states.last!) {
case (.loading, .loaded):
break
default:
XCTFail()
}
}
}
final class FeedDetailsViewModelTests: XCTestCase {
// …

func testLoadDataTransitionsToLoadedState() {
viewModel.loadData()
switch (states.first!, states.last!) {
case (.loading, .loaded):
break
default:
XCTFail()
}
}
}
final class FeedDetailsViewModelTests: XCTestCase {
// …

func testFeedItemData() {
viewModel.loadData()
switch states.last! {
case .loaded(let data):
XCTAssertEqual(data, self.feedService.mockFeedItems)
default:
XCTFail("Expected `loaded`")
}
}
}
final class FeedDetailsViewModelTests: XCTestCase {
// …

func testFeedItemData() {
viewModel.loadData()
switch states.last! {
case .loaded(let data):
XCTAssertEqual(data, self.feedService.mockFeedItems)
default:
XCTFail("Expected `loaded`")
}
}
}
final class FeedDetailsViewModelTests: XCTestCase {
// …

func testFeedItemData() {
viewModel.loadData()
switch states.last! {
case .loaded(let data):
XCTAssertEqual(data, self.feedService.mockFeedItems)
default:
XCTFail("Expected `loaded`")
}
}
}
final class FeedDetailsViewModelTests: XCTestCase {
// …

func testFeedItemData() {
viewModel.loadData()
switch states.last! {
case .loaded(let data):
XCTAssertEqual(data, self.feedService.mockFeedItems)
default:
XCTFail("Expected `loaded`")
}
}
}
Rinse and

repeat.
Dependencyji
Stub / mock
Preuzeti kontrolu nad
dependencyjem
Lakše testiranje
1.
Dependency

Injection
class PersistenceService {
var token: String? {
let defaults = UserDefaults.standard
return defaults.object(forKey: "user_token") as? String
}
}
class PersistenceService {
var token: String? {
let defaults = UserDefaults.standard
return defaults.object(forKey: "user_token") as? String
}
}
Implicitni dependency
class PersistenceService {
private let defaults: UserDefaults
init(_ defaults: UserDefaults) {
self.defaults = defaults
}
var token: String? {
return defaults.object(forKey: "user_token") as? String
}
}
Pozivatelj

kreira 

dependency
2.
Dependency

Inversion
class PersistenceService {
private let defaults: UserDefaults
init(_ defaults: UserDefaults) {
self.defaults = defaults
}
var token: String? {
return defaults.object(forKey: "user_token") as? String
}
}
protocol ObjectStore {
func set(value: Any?, forKey: String)
func object(forKey: String)-> Any?
}
protocol ObjectStore {
func set(value: Any?, forKey: String)
func object(forKey: String)-> Any?
}
extension UserDefaults: ObjectStore {}
class PersistenceService {
private let defaults: ObjectStore
init(_ defaults: ObjectStore) {
self.defaults = defaults
}
var token: String? {
return defaults.object(forKey: "user_token") as? String
}
}
class UserDefaultsMock: ObjectStore {
func object(forKey: String) -> Any? {
}
func set(value: Any?, forKey: String) {
}
}
let userDefaultsMock = UserDefaultsMock()
let persistenceService = PersistenceService(userDefaultsMock)
class UserDefaultsMock: ObjectStore {
var data: [String: Any?] = [:]
func object(forKey key: String) -> Any? {
return data[key] ?? nil
}
func set(value: Any?, forKey key: String) {
data[key] = value
}
}
final class PersistenceServiceTests: XCTestCase {
var store: ObjectStoreMock!
var service: PersistenceService!
override func setUp() {
super.setUp()
store = ObjectStoreMock()
service = PersistenceService(store)
}
func testRetrievesSavedTokenFromStore() {
store.data["token"] = "savedToken"
XCTAssertEqual(service.token, "savedToken")
}
}
Pisanje
mockova.
class LoginServiceMock: LoginServiceProtocol {
func login(username: String, password: String,
completion: @escaping (LoginResult)-> Void) {
}
}
class LoginServiceMock: LoginServiceProtocol {
var loginParameters: (username: String, password: String)?
func login(username: String, password: String,
completion: @escaping (LoginResult)-> Void) {
loginParameters = (username, password)
}
}
class LoginServiceMock: LoginServiceProtocol {
var loginParameters: (username: String, password: String)?
func login(username: String, password: String,
completion: @escaping (LoginResult)-> Void) {
loginParameters = (username, password)
}
}
class LoginServiceMock: LoginServiceProtocol {
var loginParameters: (username: String, password: String)?
func login(username: String, password: String,
completion: @escaping (LoginResult)-> Void) {
loginParameters = (username, password)
}
}
func testCallsLoginServiceWithCorrectParams() {
loginViewModel.login(

email: "email@mail.com", 

password: "123pass")
let expectedParams = ("email@mail.com", "123pass")
XCTAssertEqual(expectedParams,

loginServiceMock.loginParameters)
}
var loginShouldSucceed = true
func login(username: String, password: String,
completion: @escaping (LoginResult)-> Void) {
if loginShouldSucceed {
completion(.success(UserToken(token: "abc123")))
} else {
completion(.failure(.noInternet))
}
}
var loginShouldSucceed = true
func login(username: String, password: String,
completion: @escaping (LoginResult)-> Void) {
if loginShouldSucceed {
completion(.success(UserToken(token: "abc123")))
} else {
completion(.failure(.noInternet))
}
}
var loginShouldSucceed = true
func login(username: String, password: String,
completion: @escaping (LoginResult)-> Void) {
if loginShouldSucceed {
completion(.success(UserToken(token: "abc123")))
} else {
completion(.failure(.noInternet))
}
}
func testHandlesErrorCorrectly() {
loginServiceMock.loginShouldSucceed = false
loginViewModel.login(email: "mail@mail.com", 

password: "123pass")
XCTAssertNotNil(viewModel.errorText)
}
Sourcery je
prijatelj.
Link na kraju preze.
Pisanje testova
Testovi su
teški.
Dobri testovi
su teški.
Ne prolazi za ni jednu netočnu
implementaciju.
1. Pokrivenost
func testHandlesErrorCorrectly() {
loginServiceMock.loginShouldSucceed = false
loginViewModel.login(email: "mail@mail.com", 

password: "123pass")
XCTAssertNotNil(viewModel.errorText)
}
Prazan string?
Test prolazi za sve korektne
implementacije.
1. Lakoća
refaktoringa
func testStoresUsernameCorrectly() {
let store = UserDataStore()
store.store(username: "Marin")
XCTAssertEqual(store.storage["username"], "Marin")
}
func testStoresUsernameCorrectly() {
let store = UserDataStore()
store.store(username: "Marin")
XCTAssertEqual(store.storage["username"], "Marin")
}
func testStoresUsernameCorrectly() {
let store = UserDataStore()
store.store(username: "Marin")
XCTAssertEqual(store.storedUsername, "Marin")
}
vs
Korektno
+
Fleksibilno
Tips & tricks
Odaberi dobar
framework
Nek te CI
natjera
Piši helpere
Testiraj nešto
Whew!
Testing For People Who Hate Testing

https://eev.ee/blog/2016/08/22/testing-for-people-who-hate-testing/
AutoMockable Sourcery template

https://cdn.rawgit.com/krzysztofzablocki/Sourcery/master/docs/mocks.html
Unit Testing in Swift

https://medium.cobeisfresh.com/unit-testing-in-swift-part-1-the-philosophy-9bc85ed5001b

Mais conteúdo relacionado

Mais procurados

Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose worldFabio Collini
 
Drupal8Day: Demystifying Drupal 8 Ajax Callback commands
Drupal8Day: Demystifying Drupal 8 Ajax Callback commandsDrupal8Day: Demystifying Drupal 8 Ajax Callback commands
Drupal8Day: Demystifying Drupal 8 Ajax Callback commandsMichael Miles
 
Exercícios Netbeans - Vera Cymbron
Exercícios Netbeans - Vera CymbronExercícios Netbeans - Vera Cymbron
Exercícios Netbeans - Vera Cymbroncymbron
 
Selenium Webdriver with data driven framework
Selenium Webdriver with data driven frameworkSelenium Webdriver with data driven framework
Selenium Webdriver with data driven frameworkDavid Rajah Selvaraj
 
Simplified Android Development with Simple-Stack
Simplified Android Development with Simple-StackSimplified Android Development with Simple-Stack
Simplified Android Development with Simple-StackGabor Varadi
 
Net Beans Codes for Student Portal
Net Beans Codes for Student PortalNet Beans Codes for Student Portal
Net Beans Codes for Student PortalPeeyush Ranjan
 
Demystifying AJAX Callback Commands in Drupal 8
Demystifying AJAX Callback Commands in Drupal 8Demystifying AJAX Callback Commands in Drupal 8
Demystifying AJAX Callback Commands in Drupal 8Michael Miles
 
Don't Make Android Bad... Again
Don't Make Android Bad... AgainDon't Make Android Bad... Again
Don't Make Android Bad... AgainPedro Vicente
 
Spring Certification Questions
Spring Certification QuestionsSpring Certification Questions
Spring Certification QuestionsSpringMockExams
 
Selenium my sql and junit user guide
Selenium my sql and junit user guideSelenium my sql and junit user guide
Selenium my sql and junit user guideFahad Shiekh
 
Build Widgets
Build WidgetsBuild Widgets
Build Widgetsscottw
 
E2E testing con nightwatch.js - Drupalcamp Spain 2018
E2E testing con nightwatch.js  - Drupalcamp Spain 2018E2E testing con nightwatch.js  - Drupalcamp Spain 2018
E2E testing con nightwatch.js - Drupalcamp Spain 2018Salvador Molina (Slv_)
 
Advanced java practical semester 6_computer science
Advanced java practical semester 6_computer scienceAdvanced java practical semester 6_computer science
Advanced java practical semester 6_computer scienceNiraj Bharambe
 
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...탑크리에듀(구로디지털단지역3번출구 2분거리)
 
Converting Db Schema Into Uml Classes
Converting Db Schema Into Uml ClassesConverting Db Schema Into Uml Classes
Converting Db Schema Into Uml ClassesKaniska Mandal
 
Sustaining Test-Driven Development
Sustaining Test-Driven DevelopmentSustaining Test-Driven Development
Sustaining Test-Driven DevelopmentAgileOnTheBeach
 
Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3 Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3 DreamLab
 
Demystifying Drupal AJAX Callback Commands
Demystifying Drupal AJAX Callback CommandsDemystifying Drupal AJAX Callback Commands
Demystifying Drupal AJAX Callback CommandsMichael Miles
 

Mais procurados (20)

Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose world
 
Drupal8Day: Demystifying Drupal 8 Ajax Callback commands
Drupal8Day: Demystifying Drupal 8 Ajax Callback commandsDrupal8Day: Demystifying Drupal 8 Ajax Callback commands
Drupal8Day: Demystifying Drupal 8 Ajax Callback commands
 
Exercícios Netbeans - Vera Cymbron
Exercícios Netbeans - Vera CymbronExercícios Netbeans - Vera Cymbron
Exercícios Netbeans - Vera Cymbron
 
Selenium Webdriver with data driven framework
Selenium Webdriver with data driven frameworkSelenium Webdriver with data driven framework
Selenium Webdriver with data driven framework
 
Simplified Android Development with Simple-Stack
Simplified Android Development with Simple-StackSimplified Android Development with Simple-Stack
Simplified Android Development with Simple-Stack
 
Net Beans Codes for Student Portal
Net Beans Codes for Student PortalNet Beans Codes for Student Portal
Net Beans Codes for Student Portal
 
Demystifying AJAX Callback Commands in Drupal 8
Demystifying AJAX Callback Commands in Drupal 8Demystifying AJAX Callback Commands in Drupal 8
Demystifying AJAX Callback Commands in Drupal 8
 
Don't Make Android Bad... Again
Don't Make Android Bad... AgainDon't Make Android Bad... Again
Don't Make Android Bad... Again
 
Spring Certification Questions
Spring Certification QuestionsSpring Certification Questions
Spring Certification Questions
 
Selenium my sql and junit user guide
Selenium my sql and junit user guideSelenium my sql and junit user guide
Selenium my sql and junit user guide
 
Build Widgets
Build WidgetsBuild Widgets
Build Widgets
 
E2E testing con nightwatch.js - Drupalcamp Spain 2018
E2E testing con nightwatch.js  - Drupalcamp Spain 2018E2E testing con nightwatch.js  - Drupalcamp Spain 2018
E2E testing con nightwatch.js - Drupalcamp Spain 2018
 
Introduction to Redux
Introduction to ReduxIntroduction to Redux
Introduction to Redux
 
React lecture
React lectureReact lecture
React lecture
 
Advanced java practical semester 6_computer science
Advanced java practical semester 6_computer scienceAdvanced java practical semester 6_computer science
Advanced java practical semester 6_computer science
 
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
 
Converting Db Schema Into Uml Classes
Converting Db Schema Into Uml ClassesConverting Db Schema Into Uml Classes
Converting Db Schema Into Uml Classes
 
Sustaining Test-Driven Development
Sustaining Test-Driven DevelopmentSustaining Test-Driven Development
Sustaining Test-Driven Development
 
Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3 Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3
 
Demystifying Drupal AJAX Callback Commands
Demystifying Drupal AJAX Callback CommandsDemystifying Drupal AJAX Callback Commands
Demystifying Drupal AJAX Callback Commands
 

Semelhante a iOS Talks 6: Unit Testing

Writing Your App Swiftly
Writing Your App SwiftlyWriting Your App Swiftly
Writing Your App SwiftlySommer Panage
 
React state managmenet with Redux
React state managmenet with ReduxReact state managmenet with Redux
React state managmenet with ReduxVedran Blaženka
 
Controllers & actions
Controllers & actionsControllers & actions
Controllers & actionsEyal Vardi
 
Building Testable Reactive Apps with MVI
Building Testable Reactive Apps with MVIBuilding Testable Reactive Apps with MVI
Building Testable Reactive Apps with MVIJames Shvarts
 
Cocoa heads testing and viewcontrollers
Cocoa heads testing and viewcontrollersCocoa heads testing and viewcontrollers
Cocoa heads testing and viewcontrollersStijn Willems
 
Asp.net mvc training
Asp.net mvc trainingAsp.net mvc training
Asp.net mvc trainingicubesystem
 
Swift Delhi: Practical POP
Swift Delhi: Practical POPSwift Delhi: Practical POP
Swift Delhi: Practical POPNatasha Murashev
 
Core Data with Swift 3.0
Core Data with Swift 3.0Core Data with Swift 3.0
Core Data with Swift 3.0Korhan Bircan
 
Single page webapps & javascript-testing
Single page webapps & javascript-testingSingle page webapps & javascript-testing
Single page webapps & javascript-testingsmontanari
 
JDBC for CSQL Database
JDBC for CSQL DatabaseJDBC for CSQL Database
JDBC for CSQL Databasejitendral
 
20150516 modern web_conf_tw
20150516 modern web_conf_tw20150516 modern web_conf_tw
20150516 modern web_conf_twTse-Ching Ho
 
Windows Store app using XAML and C#: Enterprise Product Development
Windows Store app using XAML and C#: Enterprise Product Development Windows Store app using XAML and C#: Enterprise Product Development
Windows Store app using XAML and C#: Enterprise Product Development Mahmoud Hamed Mahmoud
 
[Final] ReactJS presentation
[Final] ReactJS presentation[Final] ReactJS presentation
[Final] ReactJS presentation洪 鹏发
 
Event Sourcing - what could go wrong - Devoxx BE
Event Sourcing - what could go wrong - Devoxx BEEvent Sourcing - what could go wrong - Devoxx BE
Event Sourcing - what could go wrong - Devoxx BEAndrzej Ludwikowski
 
Easy tests with Selenide and Easyb
Easy tests with Selenide and EasybEasy tests with Selenide and Easyb
Easy tests with Selenide and EasybIakiv Kramarenko
 
Conditional fields - Olga Riabodzei
Conditional fields - Olga RiabodzeiConditional fields - Olga Riabodzei
Conditional fields - Olga RiabodzeiDrupalCamp Kyiv
 
Unit test candidate solutions
Unit test candidate solutionsUnit test candidate solutions
Unit test candidate solutionsbenewu
 
React + Redux. Best practices
React + Redux.  Best practicesReact + Redux.  Best practices
React + Redux. Best practicesClickky
 

Semelhante a iOS Talks 6: Unit Testing (20)

Writing Your App Swiftly
Writing Your App SwiftlyWriting Your App Swiftly
Writing Your App Swiftly
 
Asp.NET MVC
Asp.NET MVCAsp.NET MVC
Asp.NET MVC
 
React state managmenet with Redux
React state managmenet with ReduxReact state managmenet with Redux
React state managmenet with Redux
 
Controllers & actions
Controllers & actionsControllers & actions
Controllers & actions
 
Building Testable Reactive Apps with MVI
Building Testable Reactive Apps with MVIBuilding Testable Reactive Apps with MVI
Building Testable Reactive Apps with MVI
 
Cocoa heads testing and viewcontrollers
Cocoa heads testing and viewcontrollersCocoa heads testing and viewcontrollers
Cocoa heads testing and viewcontrollers
 
Asp.net mvc training
Asp.net mvc trainingAsp.net mvc training
Asp.net mvc training
 
Swift Delhi: Practical POP
Swift Delhi: Practical POPSwift Delhi: Practical POP
Swift Delhi: Practical POP
 
Core Data with Swift 3.0
Core Data with Swift 3.0Core Data with Swift 3.0
Core Data with Swift 3.0
 
Single page webapps & javascript-testing
Single page webapps & javascript-testingSingle page webapps & javascript-testing
Single page webapps & javascript-testing
 
JDBC for CSQL Database
JDBC for CSQL DatabaseJDBC for CSQL Database
JDBC for CSQL Database
 
20150516 modern web_conf_tw
20150516 modern web_conf_tw20150516 modern web_conf_tw
20150516 modern web_conf_tw
 
Windows Store app using XAML and C#: Enterprise Product Development
Windows Store app using XAML and C#: Enterprise Product Development Windows Store app using XAML and C#: Enterprise Product Development
Windows Store app using XAML and C#: Enterprise Product Development
 
[Final] ReactJS presentation
[Final] ReactJS presentation[Final] ReactJS presentation
[Final] ReactJS presentation
 
Event Sourcing - what could go wrong - Devoxx BE
Event Sourcing - what could go wrong - Devoxx BEEvent Sourcing - what could go wrong - Devoxx BE
Event Sourcing - what could go wrong - Devoxx BE
 
Easy tests with Selenide and Easyb
Easy tests with Selenide and EasybEasy tests with Selenide and Easyb
Easy tests with Selenide and Easyb
 
Conditional fields - Olga Riabodzei
Conditional fields - Olga RiabodzeiConditional fields - Olga Riabodzei
Conditional fields - Olga Riabodzei
 
Unit test candidate solutions
Unit test candidate solutionsUnit test candidate solutions
Unit test candidate solutions
 
React + Redux. Best practices
React + Redux.  Best practicesReact + Redux.  Best practices
React + Redux. Best practices
 
Ngrx slides
Ngrx slidesNgrx slides
Ngrx slides
 

Último

Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionSolGuruz
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providermohitmore19
 
Clustering techniques data mining book ....
Clustering techniques data mining book ....Clustering techniques data mining book ....
Clustering techniques data mining book ....ShaimaaMohamedGalal
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxComplianceQuest1
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...panagenda
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️anilsa9823
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comFatema Valibhai
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxbodapatigopi8531
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsAndolasoft Inc
 

Último (20)

Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with Precision
 
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS LiveVip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
Clustering techniques data mining book ....
Clustering techniques data mining book ....Clustering techniques data mining book ....
Clustering techniques data mining book ....
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
Exploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the ProcessExploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the Process
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptx
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 

iOS Talks 6: Unit Testing