Swift: Struktury

Opublikowano: 14.09.2023 - tagi: Swift Struktura Dane

Struktury w Swift

Struktury pozwalają na organizację danych i dzięki temu łatwiejsze ich zarządzanie.

Składnia prezentuje się następująco:

struct SomeStruct {
	...
}

Żeby stworzyć strukturę, użyj słowa kluczowego struct, a następnie podajesz jej nazwę.

W ciele struktury określasz jej właściwości. Możesz też dodać do niej funkcje. W kontekście struktura funkcje nazywane są metodami.

struct Product {
	var name: String = ""
	var price: Float = 0.0
	
	func describe() {
		print("Product name: \(name) and price is: \(price)")
	}
}

Tworzenie instancji

Tworzenie instancji struktury wygląda tak:

var product = Product()

product.name = "Some product"
product.price = 9.99

product.describe() // Product name: Some product and price is: 9.99

Można też określić wartości struktury w momencie jej tworzenia:

var product = Product(name: "Some product", price: 9.99)

product.describe() // Product name: Some product and price is: 9.99

Uwaga: ważna jest kolejność parametrów. Jeśli pierwszą właściwością w strukturze jest: A, a potem B podczas tworzenia instancji struktury musisz podać wartości, w takiej kolejności.

Czyli w przypadku Product nie możesz napisać tak:

var product = Product(price: 9.99, name: "Some product") // błąd!

Możesz do tworzenia instancji struktury także użyć metody init:

struct Product {
	var name: String
	var price: Float
	
	init(name: String, price: Float) {
		self.name = name
		self.price = price
	}
	
	func describe() {
		print("Product name: \(name) and price is: \(price)")
	}
}

Dzięki tej metodzie sam możesz określić kolejność podawanych argumentów przy tworzeniu instancji struktury.

Metody

Jeśli potrzebujesz dodać do struktury metodę, która zmieni swój stan, nie możesz napisać tego w taki sposób:

struct Cart {
    var products: [Product] = []

    func addProduct(_ product: Product) {
        products.append(product)
    }
}

var product = Product(name: "Some product", price: 19.99)

var cart = Cart()

cart.addProduct(product) // błąd!

Dlaczego? W Swift struktury są niezmienne ang.: immutable. Oznacza to, że z poziomu struktury nie możesz zmienić jej stanu. No nie do końca.

Wystarczy, że do danej metody dodasz słowo kluczowe mutating:

struct Cart {
    var products: [Product] = []

    mutating func addProduct(_ product: Product) {
        products.append(product)
    }
}

var product = Product(name: "Some product", price: 19.99)

var cart = Cart()

cart.addProduct(product)

print(cart.products.count) // 1

Stałe struktury

Jeśli instancję struktury określisz za pomocą let:

let product = Product(name: "Some product", price: 19.99)

nie możesz napisać tak:

product.name = "Extra product" // błąd!

Za pomocą let mówisz Swift, że stan obiektu nie może się zmienić.


React Testing Library: Jak wyczyścić dane z kontrolki?

Opublikowano: 12.09.2023 - tagi: JavaScript React Testowanie Test Komponent Formularz Kontrolka

Usuwanie danych

Załóżmy, że testujesz przypadek: formularz jest wypełniony danymi i potrzebujesz przed sprawdzeniem testu, wyczyścić daną kontrolkę. Jak to zrobić?

Komponent:

import {useState} from "react";

export function MyComponent({ userName }: { userName: string}) {
    const [name, setName] = useState(userName);

    const onInputChange = e => {
        setName(e.target.value);
    }

    return (
        <>
            { name &&
                <div>
                    Hello {name}! How are you?
                </div>
            }
            <div>
                <label htmlFor="name">Your name:</label>
                <input value={name} id="name" name="name" onChange={onInputChange} />
            </div>
        </>
    )
}

Test:

import {render, screen} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import {MyComponent} from "./MyComponent";

test('should edit user name', async () => {
    // given
    render(<MyComponent userName="Iwona" />);
    const input = getByDisplayValue('Iwona');
    await userEvent.clear(input);

    // when
    await userEvent.type(input, 'Adam');

    // then
    expect(getByText('Hello Adam! How are you?')).toBeInTheDocument();
});

Na starcie przekazywana jest wartość do komponentu, która jest przypisana jest do kontrolki.

Żeby wprowadzić nową wartość, musimy kontrolkę wyczyścić. Robi się to za pomocą metody: clear z biblioteki: user-event. Jeśli zakomentujesz tę linijkę wartość kontrolki po wywołaniu metody: type będzie: "IwonaAdam".


React: Material UI: Jak ustawić kontrolkę na pełną szerokość?

Opublikowano: 09.09.2023 - tagi: React UI Rozmiar Formularz Kontrolka

Kontrolka na full, jak?

Żeby ustawić daną kontrolką na pełną szerokość, wystarczy ustawić jej atrybut: fullWidth.

Przykład:

import TextField from '@mui/material/TextField';

<TextField  fullWidth variant="outlined" />

Swift: Closure

Opublikowano: 07.09.2023 - tagi: Swift Funkcje

Czym jest Closure w Swift?

Closure w Swift to funkcja, która nie posiada nazwy.

let myFunction = {
	print("Hello!")
}

myFunction() // Hello!

Składnia Closure

Składnia prezentuje się następująco:

{ (parameters) -> returnType in
   ...
}

Tak jak w "zwykłej" funkcji, ta typu Closure może posiadać parametry i możesz określić zwracany typ danych.

let calculate = { (a: Int, b: Int) -> Int in
	return a + b
}

print(calculate(4, 3)) // 7

Zauważ, że w momencie wywołania funkcji typu Closure nie musimy podawać nazw parametrów, tak ja w "zwykłej" funkcji:

func substract(a: Int, b: Int) -> Int {
	return a - b
}

let add = { (a: Int, b: Int) -> Int in
	return a + b
}

print(add(4, 3)) // 7
print(substract(a: 10, b: 3)) // 7

print(substract(10, 3)) // błąd!

Closure jako parametr funkcji

Możesz przekazać do funkcji inną funkcję, która jest typu Closure:

let substract = { (a: Int, b: Int) -> Int in
	return a - b
}

let add = { (a: Int, b: Int) -> Int in
	return a + b
}

func calculate(a: Int, b: Int, operate: (_ a: Int, _ b: Int) -> (Int)) -> Int {
  return operate(a, b);
}

print(calculate(a: 5, b: 5, operate: add)) // 10
print(calculate(a: 5, b: 5, operate: substract)) // 0

Trailing Closure

Jeśli funkcja przyjmuje kilka parametrów i ostatnim z nich jest Closure można wywołać ją w inny sposób.

"Standardowa" wersja:

func calculate(a: Int, b: Int, operate: (_ a: Int, _ b: Int) -> (Int)) -> Int {
  return operate(a, b);
}

let result = calculate(a: 5, b: 5, operate: { (a: Int, b: Int) -> Int in
  return a + b
})

print(result) // 10

Można wywołać to też tak:

let result = calculate(a: 5, b: 5) { (a, b) in
  return a + b
}

print(result) // 10

Takie wywołanie nazwy się trailing closure.

Uproszczona składnia

Poniżej znajdziesz kilka sposobów wywołania Closure.

func calculate(a: Int, b: Int, operate: (_ a: Int, _ b: Int) -> (Int)) -> Int {
  return operate(a, b);
}

Przykłady wywołania Closure:

calculate(a: 5, b: 5, operate: { (a: Int, b: Int) -> Int in
  return a + b
})

Można krócej:

calculate(a: 5, b: 5) { (a, b) in
  return a + b
}

Inna wersja:

calculate(a: 5, b: 5) {
  a, b in a + b
}

Jeszcze prostsza wersja:

calculate(a: 5, b: 5) {
  $0 + $1
}

$0 i $1 to referencje do parametrów Closure.

Uwaga! Można jeszcze to zapisać jeszcze inaczej:

calculate(a: 5, b: 5, operate: +)

React Testing Library: Jak zasymulować wpisanie danych do kontrolki?

Opublikowano: 05.09.2023 - tagi: JavaScript React Testowanie Komponent Pisanie Formularz

Testowanie wprowadzenia danych

Kiedy potrzebujesz stestować komponent, w którym zachodzą interakcje z użytkownikiem, warto napisać test, który zasymuluje cały proces, tak jakby robił to użytkownik. Możesz to zrobić za pomocą biblioteki user-event

Przykład

Komponent:

export function MyComponent() {
    const [name, setName] = useState('');

    const onInputChange = e => {
        setName(e.target.value);
    }

    return (
        <>
            { name &&
                <div>
                    Hello {name}! How are you?
                </div>
            }
            <div>
                <label htmlFor="name">Your name:</label>
                <input value={name} id="name" name="name" onChange={onInputChange} />
            </div>
        </>
    )
}

Kiedy użytkownik wpisze jakieś dane w pole tekstowe, na ekranie wyświetlony zostanie tekst.

Jak to przetestować?

import {render, screen} from '@testing-library/react';
import userEvent from '@testing-library/user-event';

const { getByLabelText, getByText } = screen;

test('should display greeting', async () => {
    // given
    render(<MyComponent />);
    const inputName = getByLabelText('Your name:');

    // when
    await userEvent.type(inputName, 'Kacper');

    // then
    expect(getByText('Hello Kacper! How are you?')).toBeInTheDocument();
});

Za pomocą getByLabelText i getByText pobieramy referencje do pola tekstowego i miejsca, gdzie tekst ma się wyświetlić po wpisaniu danych.

Za pomocą type z biblioteki user-event wprowadzany jest tekst do pola tekstowego. Pierwszy argument to referencja do kontrolki, dla której wpisujemy dane. Drugi argument to tekst, który chcemy przekazać.