## 1. Project Overview & Quickstart (orhun/LICENSE-APACHE)

# LICENSE-APACHE

Open-source repository orhun/LICENSE-APACHE

### Repository Details
- **Repository:** [orhun/LICENSE-APACHE](https://github.com/orhun/LICENSE-APACHE)
- **Primary Language:** Code

*Note: High-volume repository documentation is actively indexed and synchronized by YakaAI.*

## 2. Official Technical Reference & Guides (orhun/website)

## File: README.md

## Personel Web Site



### Technologies I use

- [Svelte](https://svelte.dev/)
- [SvelteKit](https://kit.svelte.dev/)
- [Tailwind](https://tailwindcss.com/)
- [MDsveX](https://mdsvex.pngwn.io/)
- [Notion.io](https://notion.so/)
- [Upstash](https://upstash.com/)
- [Raindrop.io](https://raindrop.io/)

---

## File: package.json

{
  "name": "faruk-website",
  "version": "0.0.1",
  "scripts": {
    "dev": "svelte-kit dev",
    "build": "svelte-kit build",
    "package": "svelte-kit package",
    "preview": "svelte-kit preview",
    "prepare": "svelte-kit sync",
    "check": "svelte-check --tsconfig ./tsconfig.json",
    "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
    "lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
    "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ."
  },
  "devDependencies": {
    "@sveltejs/adapter-auto": "next",
    "@sveltejs/kit": "next",
    "@types/node": "^17.0.21",
    "@typescript-eslint/eslint-plugin": "^5.10.1",
    "@typescript-eslint/parser": "^5.10.1",
    "eslint": "^7.32.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-svelte3": "^3.2.1",
    "prettier": "^2.5.1",
    "prettier-plugin-svelte": "^2.5.0",
    "svelte": "^3.44.0",
    "svelte-check": "^2.2.6",
    "svelte-preprocess": "^4.10.1",
    "tslib": "^2.3.1",
    "typescript": "~4.6.2"
  },
  "type": "module",
  "dependencies": {
    "@notionhq/client": "^1.0.2",
    "@sveltejs/adapter-static": "^1.0.0-next.29",
    "@sveltejs/adapter-vercel": "1.0.0-next.46",
    "@tailwindcss/typography": "^0.5.2",
    "@upstash/redis": "^1.0.1",
    "autoprefixer": "^10.4.2",
    "dayjs": "^1.11.0",
    "mdsvex": "^0.10.5",
    "mdsvex-relative-images": "^1.0.1",
    "notion-to-md": "^2.3.2",
    "postcss": "^8.4.8",
    "reading-time": "^1.5.0",
    "rehype-figure": "^1.0.1",
    "remark-code-titles": "^0.1.2",
    "remark-heading-id": "^1.0.0",
    "sass": "^1.49.9",
    "tailwindcss": "^3.0.23"
  }
}

---

## File: tsconfig.json

{
	"compilerOptions": {
		"moduleResolution": "node",
		"module": "es2020",
		"lib": ["es2020", "DOM"],
		"target": "es2020",
		"importsNotUsedAsValues": "error",
		"preserveValueImports": true,
		"isolatedModules": true,
		"resolveJsonModule": true,
		"sourceMap": true,
		"esModuleInterop": true,
		"skipLibCheck": true,
		"forceConsistentCasingInFileNames": true,
		"baseUrl": ".",
		"allowJs": true,
		"checkJs": true,
		"paths": {
			"$lib": ["src/lib"],
			"$lib/*": ["src/lib/*"]
		},
		"rootDirs": ["./types"]
	}
}

---

## File: src/routes/index.md

<script>
	import TechStack from '$lib/components/TechStack.svelte';
</script>

<svelte:head>

<title>Faruk Oruç</title>

</svelte:head>

# Ömer Faruk Oruç

Hi, I am Faruk, a frontend developer. Working at [Binalyze](https://www.binalyze.com/)

Creator of the open source projects. [Github CV](https://github.com/bufgix/github-cv), [Passwall Extension](https://github.com/passwall/passwall-extension), [Gihub Blog](https://github.com/bufgix/github-blog) and [React Native Notify](https://github.com/Papyon-Apps/rn-notify).
I created mobile apps with React Native for [PapyonApps](https://github.com/Papyon-Apps/)

<TechStack />

Find me on [Github](https://github.com/bufgix)
Mail me at [hi@farukoruc.com](mailto:hi@farukoruc.com])

You can see my full [resume](/resume.pdf)

---

## File: src/routes/blog/artik-pythonda-elif-yazmak-zorunda-degilsiniz/index.md

---
title: Artık Python'da `elif` yazmak zorunda değilsiniz!
date: 2021-11-26
tags:
  - Python
  - CPython
  - Grammar
---

Twitterda gezinirken şöyle bir şey gördüm.




Python yüzünden dünya bi data engineer kaybetti 
[pic.twitter.com/t1W8392Nrz](https://t.co/t1W8392Nrz)
&mdash; Ilter (@koseilteer) 
[November 20, 2021](https://twitter.com/koseilteer/status/1461979376199819266?ref_src=twsrc%5Etfw)


[İlgili Tweet](https://twitter.com/koseilteer/status/1461979376199819266)

Insanların bir keyword yüzünden Pythonu bırakmasına gönlüm razı olmadı (!)
Bu yüzden bu `elif` keywordunu Python da `elseif` ile değişmeye karar verdim.

### Neden?

Makalenin amacı troll olsa da Python'u kaynak kodu üzerinden derleyip içeride ne döndüğüne bakmak, bu build
aşamalarının ne olduğunu görmek yapmak istediğim bir şeydi. Bir frontend dev olarak bu işler nasıl oluyor bakmak istedim.

### CPython'ı Derlemek

Şimdi şunu belirtmekte fayda var, terminalinize gelip `python` yazdığınızda çalışan şey %90 ihtimalle CPython. Peki ne bu CPython?
En kısa tanımı ile, Pythonun bir implementasyonu. Şöyle ki Python interpreteri de Python kodlarını çalıştıran bir program. CPython ise
bu programın C ile yazılmış versiyonu. Pek çok version yazılmış tabii ki ama en çok kullanılan bu version. CPythonun kaynak kodları
[burada](https://github.com/python/cpython) bulunuyor. Buradan kendinize bir fork çakıp o forku bilgisayarınıza klonlayın. Ben kendi adıma
klonlamıştım.

```bash
git clone https://github.com/bufgix/cpython
cd cpython
```

Mac bir cihaz kullandığım için yapmam gereken şu

```bash
brew install openssl xz gdbm
```

sonrasında `configure` scriptini çalıştıralım

```bash
./configure --with-pydebug --with-openssl=$(brew --prefix openssl)
```

daha sonra build almaya geçebiliriz.

```bash
make -s -j2
```

`-j2` flagı `make` in aynı anda 2 işi yapmabilmesini sağlar. Eğer 4 çekirdeğiniz varsa `-j4` diyebilirsiniz.
Bu noktadan itibaren bilgisayarınızın fan seslerini ve `tkinter` gibi paketlerinden çıkan hataları görmezden gelebilirsiniz.

İşlem bittiğinde tertemiz Python 3.11 binary si çıkmış olacak. Hemen test edelim

```bash
~/cpython on main
❯ ./python.exe
Python 3.11.0a2+ (heads/main-dirty:253b7a0a9f, Nov 26 2021, 15:26:42) [Clang 12.0.5 (clang-1205.0.22.11)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
```

Nice!

### Dile keyword eklemek

Aslında yapacağımız şey dile yep yeni bir keyword eklemek değil. Var olan bir keywordün ismini değiştirmek. Dolayısıyla
işimiz çok daha kolay.

Python dilin syntaxını belirtmek için `Grammar/python.gram` dosyasını kullanıyor. Dil ile ilgili bir yazımı değiştirmek istediğimizde
bu dosyayı değiştirmek gerekiyor.

Dokümantasyona bakmadan ilk aklıma gelen IDE de `elif` keywordunu bulup bunu `elseif` değiştirmekti. 354. satırın oraları şöyle değiştirdim.

```gram {8-10}
if_stmt[stmt_ty]:
    | invalid_if_stmt
    | 'if' a=named_expression ':' b=block c=elif_stmt {
        _PyAST_If(a, b, CHECK(asdl_stmt_seq*, _PyPegen_singleton_seq(p, c)), EXTRA) }
    | 'if' a=named_expression ':' b=block c=[else_block] { _PyAST_If(a, b, c, EXTRA) }
elif_stmt[stmt_ty]:
    | invalid_elif_stmt
    | 'elif' a=named_expression ':' b=block c=elif_stmt {
        _PyAST_If(a, b, CHECK(asdl_stmt_seq*, _PyPegen_singleton_seq(p, c)), EXTRA) }
    | 'elif' a=named_expression ':' b=block c=[else_block] { _PyAST_If(a, b, c, EXTRA) }
else_block[asdl_stmt_seq*]:
    | invalid_else_stmt
    | 'else' &&':' b=block { b }

```

---

```gram {8-10}
if_stmt[stmt_ty]:
    | invalid_if_stmt
    | 'if' a=named_expression ':' b=block c=elif_stmt {
        _PyAST_If(a, b, CHECK(asdl_stmt_seq*, _PyPegen_singleton_seq(p, c)), EXTRA) }
    | 'if' a=named_expression ':' b=block c=[else_block] { _PyAST_If(a, b, c, EXTRA) }
elif_stmt[stmt_ty]:
    | invalid_elif_stmt
    | 'elseif' a=named_expression ':' b=block c=elif_stmt {
        _PyAST_If(a, b, CHECK(asdl_stmt_seq*, _PyPegen_singleton_seq(p, c)), EXTRA) }
    | 'elseif' a=named_expression ':' b=block c=[else_block] { _PyAST_If(a, b, c, EXTRA) }
else_block[asdl_stmt_seq*]:
    | invalid_else_stmt
    | 'else' &&':' b=block { b }

#  1220 li satırlara doğru ise
invalid_elif_stmt:
    | 'elseif' named_expression NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") }
    | a='elseif' named_expression ':' NEWLINE !INDENT {
        RAISE_INDENTATION_ERROR("expected an indented block after 'elif' statement on line %d", a->lineno) }

```

Bu dosyayı her değiştiriğimzde `make regen-pegen` komutu ile `parser.c` dosyasının bu grammar file ile
yeniden generate edilmesini sağlamalıyız.

```bash
~/cpython on main
❯ make regen-pegen
PYTHONPATH=./Tools/peg_generator python3.9 -m pegen -q c \
		./Grammar/python.gram \
		./Grammar/Tokens \
		-o ./Parser/parser.new.c
python3.9 ./Tools/scripts/update_file.py ./Parser/parser.c ./Parser/parser.new.c

```

Güzel şimdi tekrar build alalım.

```bash
make -s -j8
```

Ve o da ne

```bash
~/cpython on main ⇡1 !2
❯ make -j8 -s
ld: warning: directory not found for option '-L/usr/local/opt/zlib/lib'
  File "<frozen importlib._bootstrap_external>", line 108
    elif new_root.endswith(':'):
    ^^^^
SyntaxError: invalid syntax
make: *** [Python/frozen_modules/importlib._bootstrap_external.h] Error 1
make: *** Waiting for unfinished jobs....
  File "<frozen importlib._bootstrap>", line 299
    elif hasattr(loader, 'module_repr'):
    ^^^^
SyntaxError: invalid syntax
make: *** [Python/frozen_modules/importlib._bootstrap.h] Error 1
  File "<frozen ntpath>", line 99
    elif p_drive and p_drive != result_drive:
    ^^^^
SyntaxError: invalid syntax
  File "<frozen _collections_abc>", line 435
    elif not _is_param_expr(t_args):
    ^^^^
SyntaxError: invalid syntax
make: *** [Python/frozen_modules/ntpath.h] Error 1
  File "<frozen posixpath>", line 85
    elif not path or path.endswith(sep):
    ^^^^
SyntaxError: invalid syntax
make: *** [Python/frozen_modules/_collections_abc.h] Error 1
make: *** [Python/frozen_modules/posixpath.h] Error 1
  File "<frozen genericpath>", line 149
    elif isinstance(s, bytes):
    ^^^^
SyntaxError: invalid syntax
make: *** [Python/frozen_modules/genericpath.h] Error 1

```

200 IQ hareket . `elif` i değiştik ama Python internal modullerinde hala `elif` kullanıyor. Dolayısıyla parser hata çıkartıyor.

Bunun çözmek için `elif` keywordunu tamamen değişmek yerine `elseif` i de kullanılabilir yapmak gerekiyor. Dokümantasyonda biraz gezindikten sonra
`|` operatoru ile bunu yapabileceğimi anlıyorum. Aynı yerlerdeki `'elseif'` stringlerini `('elif' | 'elseif')` bununla değiştiriyorum. `make regen-pegen`
ve `make -s -j8` komutunu çalıştırdığımızada.

```bash
~/cpython on main ⇡1 !2
❯ ./python.exe
Python 3.11.0a2+ (heads/main-dirty:36e1a69ba8, Nov 26 2021, 19:37:05) [Clang 12.0.5 (clang-1205.0.22.11)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> if True:
...     pass
... elseif False:
...     pass
...
>>>
```

Bingo!!

Artık rahatlıkla Python kullanabilirsiniz.

### Kaynaklar

- https://devguide.python.org/grammar/
- https://realpython.com/cpython-source-code-guide/
- https://isidentical-archive.github.io/cpython-yeni-operator.html

---

## File: src/routes/blog/building-bulletproof-react-native-applications/index.md

---
title: Kurşun Geçirmez React Native Uygulamaları Oluşturmak - I
date: 2022-01-01
tags:
  - React
  - React Native
---

Evet biliyorum _clickbait_ başlık ama son zamanlarda (_7 ay_) React Native
ile baya bi' uygulama geliştirdim. Bu süreçte öğrendiğim bazı şeyleri paylaşmak istiyorum.
Burada anlatacağım her şey belki en doğru yöntem değil fakat React Native projelerimi
bu şekilde oluşturuyorum ve şimdiye kadar sorun yaşamadım.

Bu yazıda gelir getirmesi istenen bir uygulamanın
_uygulama-içi-satın-alma_, _abonelik_, _metadata_, _build aşamaları_, _markete vermeden update etme_ gibi
konuları ele alacağım. Bunun yanında React tarafında
_store yapısı, hookların efektif kullanımı, API dizaynı_ gibi başlıklardan da bahsetmek istiyorum.

## Projeyi Oluşturmak

Bir React Native uygulaması oluşturmanın birden fazla yolu var. Bunlardan biri
[Expo](https://expo.dev/) kullanmak. Expo kullanmak yeni başlayan biri için oldukça kolay bir yol olabilir.
Fakat kişisel olarak ben artık tercih etmiyorum nedenleri ise:

- Uygulamanın boyutunu fazla **büyütmesi** (Bir QR kod okuyucu yapmıştım ve production
  buildi 67mb olmuştu :D. Bunu azaltmanın yolları var tabi ama o toplara girmeyi tercih etmiyorum)
- Bazı native modulleri Expo üzerinden **kullanamamak**.

Bunlar dışında çok da yok aslında ama normal React Native kurulumuyla başlamak bana
daha fazla kontrol sağlıyor. (Expo kullanıp memnun olanlar da var tabi. [Buradan](https://twitter.com/enesozt_/status/1474039877893296140)
okuyabilirsiniz. Anlaşılan Expo'nun yeni sürümleri yukarda bahsettiğim sorunları çözüyor. Yakında yeniden denerim kendisini.)

#### Typescript

Evet bu [zımbırtı](https://www.typescriptlang.org/) olmadan ben artık uygulama _yazmıyorum_. İlerde anlatacağım çoğu şeyde Typescript
yardımını kullandığım için Typescriptin olmaması kabul edilemez. Unutmayın amacımız kurşun geçirmeyen,
sürdürülebilir bir uygulama oluşturmak. Typescript bu konuda baya kolaylik sağlıyor.
bu yüzden React Native uygulamasını Typescript ile kurmanızı tavsiye ederim.

```shell
$ npx react-native init BestAppEver --template react-native-template-typescript
```

Detaylı kurulumu tabii ki React Native'in [kendi sitesinden](https://reactnative.dev/docs/environment-setup)
bulabilirsiniz.

Ayrıca ilerde proje içi tipler için bir `*.d.ts` dosyanızı oluşturmanızı tavsiye ederim.

```ts:./src/types/index.d.ts
declare global {
	type MyType = {
		name: string;
		//...
	};
}
```

Bu dosyayı oluşturduktan sonra import etmeye gerek kalmadan tiplerinizi kullanabileceksiniz. Fazlalık
importların oluşmasını önlemek için güzel bir yöntem.

### Dizin Yapısı

```shell
❯ tree -L 2 src -C --dirsfirst
src
├── api
│   ├── queries
│   └── index.ts
├── assets
│   ├── fonts
│   └── images
├── components
│   ├── icons
│   ├── Button.tsx
│   ├── Card.tsx
│   ├── CheckBox.tsx
│   ├── Page.tsx
│   ├── Text.tsx
│   └── index.ts
├── icons
│   ├── single-man.svg
│   ├── support-human.svg
│   └── video-player-movie.svg
├── lang
│   ├── en.ts
│   ├── index.ts
│   └── tr.ts
├── navigation
│   └── index.tsx
├── store
│   ├── app.slice.ts
│   └── index.ts
├── styles
│   ├── colors.ts
│   ├── fonts.ts
│   ├── helpers.ts
│   ├── index.ts
│   └── spacing.ts
├── typings
│   └── index.d.ts
├── utils
│   ├── hooks
│   ├── constants.ts
│   └── index.ts
├── views
│   ├── Main
│   ├── ScreenOne
│   └── ScreenTwo
└── bootstrap.tsx

32 directories, 92 files
```

Genel olarak böyle bir dizin yapısı kullanıyorum. Kısaca bir özet geceyim, daha sonra daha detaylı inceleyelim.

- `api` burada backend servisi ile haberleşmek için gerekli dosyalar bulunur. `Axios`, `Apollo` gibi kütühanelerin örneklemeleri burada bulunur
- `assets` burada uygulamada kullanılacak resimler ve fontlar gibi static dosyalar bulunur
- `components` burada uygulama bileşenleri bulunur
  > Tercihen ben bu dizin içinde bir de `index.ts` ekleyip tüm componentleri buradan exportluyorum. Bu, komponenti
  > kullanırken daha temiz bir kod sağlıyor
  >
  > ```ts
  > export { default as Page } from './Page';
  > export { default as Text } from './Text';
  > export { default as Card } from './Card';
  > export { default as Button } from './Button';
  > ```
  >
  > kullanırken
  >
  > ```ts
  > import { Button } from '@/components';
  > ```
- `icons` Burada svg tipinde iconlar bulunur.
- `lang` Burada dil objeleri bulunur
- `navigation` Burada sayfaların navigasyondaki yollarını belirleyen tanımlamalar bulunur
- `store` Burada uygulamanın store dosyaları bulunur
- `styles` Burada renkler, fontlar ve margin-padding değerleri bulunur.
- `typings` Burada uygulamamız için tip tanımlamaları bulunur
- `utils` Burada hooklar, yardımcı methodlar ve sabit değelerin olduğu dosyalar bulunur
- `views` Burada uygulamada kullancağımız sayfalar bulunur
- `bootstrap.tsx` Bu dosya uygulamamızın kök komponenti. Genelde burada uygulama açılmadan önce yüklencek
  asenkron işlemler(network requestleri, local databaseden inital verilerin çekilmesi vb.) yapılır. Provider'ları da
  bu dosyaya yazarım ve genelde şuna benzer:



Eğer SplashScreen kullancaksınız bütün asenkron işlemleri yaptıktan sonra Splash ekranını bu component içinde kaldırabilirsiniz.

Örnek olarak:

```tsx:./src/bootstrap.tsx
import React, { useEffect } from 'react';
import SplashScreen from 'react-native-splash-screen';

import RootStackScreen from './navigation';

export default function Bootstrap() {
	useEffect(() => {
		setupApp().then(() => SplashScreen.hide());
	}, []);

	return <RootStackScreen />;
}
```

### Iconlar ve SVG

Yaptığım uygulamaların hiçbirinde tasarımı ben yapmadım elimde her zaman bir [Figma](https://www.figma.com) dosyası oldu. Figmada
iconları `.svg` olarak export alabildiğimden kolayca React Native içerisinde kullanbiliyordum.

Bunun için kullandığım araç [SVGR](https://react-svgr.com/). SVGR nin amacı, svg dosyalarını benim için React componentlerine
dönüştürmek.

#### SVGR kurulumu

```shell
$ yarn add @svgr/cli --dev
$ yarn add react-native-svg
```

Gerekli kütüphaneleri kurduktan sonra npm scriptleri yardımıyla icon ekleme işlemini otomatikleştiriyorum

```json:package.json
 "scripts": {
     //...
    "svgr": "svgr ./src/icons -d ./src/components/icons --native --no-dimensions --typescript --svgo-config .svgorc.json",
  }
```

Kısaca yaptığı işi açıklamak gerekirse, svgr ye `./src/icon` dizinini okumasını `--native` parametresi ile `react-native-svg`
e uygun işlem yapmasını, `--no-dimesion` ile _width_ ve _height_ değerlerini kaldırmasını, `--typescript` ile Typescript uyumlu
olmasını, `--svgo-config` config dosyasını yolunu ve sonunda oluşturduğu çıktıları `./src/components/icons` dizine
çıkartmasını istediğimizi belirttik. Config dosyasında ise basitçe:

```json:./scgorc.json
{ "plugins": [{ "removeViewBox": false }] } // viewboxu kaldırmak için plugin kullandık
```

Şimdi scripti çalıştırdığımızda

```shell
❯ yarn svgr
yarn run v1.22.10
$ svgr ./src/icons -d ./src/components/icons --native --no-dimensions --typescript --svgo-config .svgorc.json
src/icons/diamond.svg -> src/components/icons/Diamond.tsx
src/icons/button-refresh.svg -> src/components/icons/ButtonRefresh.tsx
src/icons/arrow-left.svg -> src/components/icons/ArrowLeft.tsx
src/icons/heart.svg -> src/components/icons/Heart.tsx
src/icons/reload.svg -> src/components/icons/Reload.tsx
src/icons/office-file.svg -> src/components/icons/OfficeFile.tsx
src/icons/support-human.svg -> src/components/icons/SupportHuman.tsx

✨  Done in 0.98s.
```

Daha sonra istediğim iconu herhangi bir yerde kullanmak çok kolay

```tsx
import * as Icons from '@/components/icons/';

<Icons.ArrowLeft height={15} width={15} color="red" />;
```

### Boyutlar, Boşluklar

Elinizde bir tasarım varsa tasarımda elemente gelip width, height değerlerini pixel cinsiden olduğu gibi
React Native'e vermek çok mantıklı değil. Çünkü faklı ekranlarda aynı pixel değeri istediğimiz sonuçları vermeyebilir.
Bunu çözmek için kullandığım yöntem ise şöyle:

Diyelim ki referans alıdğınız bir tasarım var



Görüldüğü üzere tasarımın frame width değeri **375px**. Dolayısıyla ekrana yerlemiş olan elementler de bu genişlik
değeri baz alınarak yapılmış. Biz de tasarımı uygulamaya geçirirken buna benzer bir yöntem kullanacağız.
Bunun için React Native tarafında `PixelRatio` kullanarak ekranın genişliğine göre şöyle bir
_utility_ fonksiyon yazabiliriz

```ts:./src/utils/index.ts
import { Dimensions, PixelRatio } from 'react-native';

let screenWidth = Dimensions.get('window').width;

// https://www.npmjs.com/package/react-native-responsive-screen
function widthPercentageToDP(widthPercent) {
	// Parse string percentage input and convert it to number.
	const elemWidth = typeof widthPercent === 'number' ? widthPercent : parseFloat(widthPercent);

	// Use PixelRatio.roundToNearestPixel method in order to round the layout
	// size (dp) to the nearest one that correspons to an integer number of pixels.
	return PixelRatio.roundToNearestPixel((screenWidth * elemWidth) / 100);
}

/**
 * @param size size given in the design
 * @returns responsive size of the text
 */
export function responsiveSize(size: number) {
	return widthPercentageToDP((size / 375) * 100);
}
```

Daha sonra bu `responsiveSize` methodunu fontlarda, spacing değerlerinde kullanabilirsiniz.

```ts:./src/style/fonts.ts
import { responsiveSize as rs } from '@/utils';

export const size = {
	h1: rs(30),
	h2: rs(24),
	h3: rs(20),
	medium: rs(16),
	normal: rs(14),
	input: rs(16),
	regular: rs(14),
	small: rs(12),
	mini: rs(8)
};
```

### API dizaynı

Api dizaynı projeden projeye değişebileceği gibi bir REST API ile çalışıyorsanız endpointleri servis dosyaları şeklinde yazmak mantıklı oluyor.

```ts:/api/HTTPClient.ts
import Axios from 'axios';

let baseURL = 'https://api.product.com/';
const TOKEN = '<Token_here>';

const client = Axios.create({
	baseURL,
	headers: {
		Accept: 'application/json, text/plain, */*',
		Authorization: `TOKEN ${TOKEN}`
	}
});

export default client;
```

Diyelim ki API servisiniz size databasede olan araçları veriyor

```ts:/api/services/Cars.ts
import HTTPClient from '../HTTPClient';

export type Car = {
	name: string;
	model: string;
};

export async function fetch(limit?: number) {
	const { data } = await HTTPClient.get<Car[]>('cars/');
	return data;
}
```

Bunun gibi bir servis dosyası yazmak, hem düzeni sağlıyor hem de okunabilirliği artırıyor.

#### Side Effects

Bir API isteği yapacağımız zaman _loading_, _error_ gibi durumları da
yakalamamız ve kullanıcıya bildirmemiz gerekir.

Backend servisini basit bir istek yaptığımızı düşünelim. Sayfa açıldığı zaman
verileri çeksin

```tsx:/views/Cars/index.tsx 
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList } from 'react-native';

import * as CarService from '@/api/services/Cars';

export default function Index() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [data, setData] = useState<CategoryService.Car[]>([]);

  useEffect(() => {
    setLoading(true);
    CarService.fetch()
      .then(cars => {
        setData(cars);
      })
      .catch(e => setError(e))
      .finally(() => setLoading(false));
  }, []);

  return (
    <View>
      {loading && <Text>Loading...</Text>}
      {error && <Text>Error!</Text>}
      {data && <FlatList .... />}
    </View>
  );
}
```

Görüldüğü gibi her sayfada en az 3 tane state değişkeni tanımlamamız gerekiyor. Böyle 10 tane sayfanız oluduğunu düşünürseniz sürkeli
bu şekilde tanımlamalar yapmak işkence olacaktır. Bunu daha efektif bir hale getirebiliriz.

React tarafında backend servisinden veri çekerken [React Query](https://react-query.tanstack.com/) kullanıyorum.
Basit olarak şöyle

```tsx:/views/Cars/index.tsx 
import React from 'react';
import { View, Text, FlatList } from 'react-native';
import { useQuery } from 'react-query';

import * as CarService from '@/api/services/Cars';

export default function Index() {
  const { isLoading, isError, data } = useQuery('cars', () =>
    CarService.fetch(),
  );

  return (
    <View>
      {isLoading && <Text>Loading...</Text>}
      {isError && <Text>Error!</Text>}
      {data && <FlatList {...} />}
    </View>
  );
}
```

Yazması gerçekten keyifli oluyor tabi. Fakat React Query sadece "kodu kısaltmak" ile kalmıyor.
Caching, Memorizing, Performance Optimization gibi pek çok farklı sorunu çözüyor.

### Store Yapısı

Ben global state için uygulamarımda [Redux](https://redux.js.org/) ve [Redux-Toolkit](https://redux-toolkit.js.org/) kullanıyorum. Reduxdan önce [Mobx](https://mobx.js.org/README.html),
[Mobx-State-Tree](https://mobx-state-tree.js.org/intro/welcome) gibi kütüphaneler de kullandım. Redux o zamanlar bana bir sürü reducer yazdığın,
bir ton _boilerplate_ içeren kod yazmamın gerektiği, kötü bir kütüphane gibi geliyordu. Son zamanlarda
Redux Toolkit ile birlikte Redux yazmak olukça keyifli hale geldi. Redux Toolkit ile
daha az ve anlaşılır kod ile state'i yönetebildiğimi görünce artık projelerimde Redux kullanmaya
başladım.

Store yazarken dikkat ettiğim 2 şey var. Biri store'u typescript ile doğru ayarlamak.

```ts:/store/user.slice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";

expot type User  = {
  name: string
}

interface IUserState {
  user: User | null;
}

const initialState: IUserState = {
  user: null
};

export const userSlice = createSlice({
  name: "user",
  initialState,
  reducers: {
    setUser: (state, action: PayloadAction<User>) => {
      state.user = action.payload;
    },
  },
});

export const { setUser } = appSlice.actions;
export default userSlice.reducer;
```

```ts:/store/index.ts
import { combineReducers, configureStore } from '@reduxjs/toolkit';
import UserSlice from './user.slice';

const reducers = combineReducers({
	user: UserSlice
});

const store = configureStore({
	reducer: reducers
});

export default store;
```

Store tipine doğrudan erişmek için `index.d.ts` ye yazmak mantıklı.

```ts
import store from '@/store';

declare global {
	// Oher Types...

	// Store Types
	type RootState = ReturnType<typeof store.getState>;
	type AppDispatch = typeof store.dispatch;
}
```

Ardından React uygulamasına özel store için iki basit hook yazabiliriz.

```ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';

export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
```

Diğer dikkat ettiğim şey ise. Redux slice'larına özel hooklar yazmak. Mesela yukardaki `user`
için söyle bir hook yazılabilir.

```tsx
import { useContext } from "react";

import { useAppDispatch, useAppSelector, User } from "./useStore";
import { setUser as setUserReducer } from "@/store/user.slice";

export default function useUser() {
  const dispatch = useAppDispatch();

  const user = useAppSelector((state) => state.user);

  const setUser = (user: User) => {
    dispatch(setUserReducer(user));
  };

  return { user, setUser };
}


...

// Component içinde
function Index(){
  const user = useUser(); // Storedaki user artık burada

  return ...
}

```

Burada ilk kısmı sonlandırıyorum. Buraya kadar hep React, React Native üzerinde durdum. İkinci kısımda (_eğer üşenmeden yazarsam_) build süreçleri, versyonlama,
uygulama içi satın alma ve abonelik gibi konulara gireceğim.

---

## File: src/routes/blog/python-dataclasses/index.md

---
title: Python Dataclasses
date: 2020-11-01
tags:
  - Python
---


## Nedir?

Python 3.7 ile gelen güzel bir yapı olan `dataclass`'lar, Python geliştiricileri için mükemmel bir yenilik. Bu yazıda elimden geldiğince bu yapıyı anlatmaya çalısacağım.

Efendim `dataclass`'lar normal class'lardaki kendini sürekli tekrarlayan kodları yazmayı engelleyen, içinde (genelde) veri depolayan classlardır. Aşağıdaki örnekte bir `dataclass`'ın nasıl tanımladığını görebilirsiniz.

> Unutmayın `dataclass`'lar Python'un 3.7 sürümünde bulunur.

```python
from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int
```

Görüldüğü gibi `dataclass`'lar `@dataclass` decoratoru ile tanımlanır.

> Sınıf değişkenlerinin yanında tipinin yazıldığını fark etmişsinizdir. Bu Python'da [Type Annotations](https://docs.python.org/3/library/typing.html) olarak geçer. `dataclass`'lar bunu kullandığından dolayı bilmek önemlidir. [Şurada](https://realpython.com/python-type-checking/) type annotations ile ilgili cok güzel bir makale var.

Bir `dataclass`, bazı [special method](https://docs.python.org/3/reference/datamodel.html#special-method-names)'ları sizin için değiştirir. Mesela `__str__()` methodu otomatik olarak sınıf içindeki değerleri bastıracaktır.

```python
>>> p = Person("Ömer", 19)
>>> p
Person(name='Ömer', age=19)
>>> p.name = "Ahmet"
>>> p.age = 30
>>> p
Person(name='Ahmet', age=30)
```

Eğer biz böyle bir çıktı isteseydik şöyle bir şey yazmamız gerekirdi;

```python
class NPerson:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        return f"{self.__class__.__name__}(name={self.name!r}, age={self.age})"

    def __str__(self):
        return self.__repr__()
```

Görüldüğü gibi bir değişkeni `__init__`'den alıp nesne değişkeni yapmak için 3 kere yazmamız gerekiyor.

Ayrıca dikkatli bakarsanız objelerin tam olarak _tanımlanmadığını_ göreceksiniz.

```python
>>> p = Person("Ömer", 19)
>>> p == Person("Ömer", 19)
True
>>> np = NPerson("Ahmet", 30)
>>> np == NPerson("Ahmet", 30)
False
```

Bunun nedeni `dataclass`'ların `__eq__` methodunu override etmiş olmaları. Normalde Python, obje karşılaştırma yaparken objelerin adreslerini karşılaştırır ama `dataclass`'lar sınıf içindeki değerleri karşılaştırır. Eğer bunu kendiniz yazmak isteseydiniz;

```python
class NPerson:
    [...]
    def __eq__(self, other):
        if other.__class__ is not self.__class__:
            return NotImplemented
        return (self.name, self.age) == (other.name, other.age)
```

`dataclass`'lar bunu bizim için yapar.

`dataclass`'lara default değerler de verebiliriz;

```python
@dataclass
class Person:
    name: str
    age: int = 18

>>> p = Person("Ahmet")
>>> p
Person(name='Ahmet', age=18)
```

> **Not:** `dataclass`'lar (ve Python) aslında değişkenlerin tipine dikkat etmez. Type annotations sadece okunabilirliği artırır.

```python
from dataclasses import dataclass
from typing import Any

@dataclass
class Person:
    name: Any
    age: str

>>> p = Person(14, "Foo")
>>> p
Person(name=14, age='Foo')
```

Şu ana kadar hiç fonksiyon yazmadık ama `dataclass`'da fonksiyon yazmak ile normalde yazmak arasında hiç fark yoktur.

```python
from dataclasses import dataclass
from typing import List

@dataclass
class Student:
    name: str
    age: int
    results: List[int]

    def average(self):
        return sum(self.results) / len(self.results)
```

```python
>>> st = Student("Ömer", 19, [85,97,67])
>>> st.average()
83.0
```

Biraz `@dataclass` decoratoru hakkında konuşalım. `@dataclass`decoratoru birçok parametre alabilir.

```python
@dataclass
class Foo:
    [...]

# Aslında buna eşittir.

@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Foo:
    [...]

```

- `init`: eğer `True` ise `__init__` fonksiyonunu override eder.

- `repr`: eğer `True` ise `__repr__` fonksiyonunu override eder.

- `eq`: eğer `True` ise `__eq__` fonksiyonunu override eder. Bu konuya yukarda değinmiştik.

- `order`: eğer `True` ise (varsayılan `False`) `__lt__`, `__le__`, `__gt__` ve `__ge__` fonksiyları override eder. Bu fonksiyonlar karşılaştırma fonksiyonlarıdır. `dataclass`, `__eq__`'de olduğu gibi class değerlerini karşılaştırır.

- `frozen`: eğer `True` ise nesne oluşturduktan sonra gelen değer atamaları `FrozenInstanceError` hatasını raise edecektir.

```python
from dataclasses import dataclass
from typing import List

@dataclass(frozen=True)
class Student:
    name: str
    age: int
    results: List[int]

    def average(self):
        return sum(self.results) / len(self.results)
```

```python
>>> st = Student("Ömer", 19, [85,97,67])
>>> st.name = "Ahmet"
---------------------------------------------------------------------------
FrozenInstanceError Traceback (most recent call last)
<file> in <module>
----> 1 st.name = "Ahmet"

<string> in __setattr__(self, name, value)

FrozenInstanceError: cannot assign to field 'name'
```

`dataclass` ile kalıtım da yapabiliriz.

```python
from dataclasses import dataclass
from typing import List

@dataclass
class Person:
    name: str
    age: int

    def say(self, s):
        print(f"{self.name}: {s}")

@dataclass
class Student(Person):
    results: List[int]

    def average(self):
        return sum(self.results) / len(self.results)
```

```python
>>> st = Student("Ömer", 19, [85,97,67])
>>> st.say("Hello world")
Ömer: Hello world
```

## Alternatifler

`dataclass`'ların (genellikle) veri depoladığını söylemiştik. Bunu Python'da sadece `dataclass`'ların yapmadığını görmüşsünüzdür. Basit veri yapıları olan `tuple` ve `dict` de veri depolar.

```python
person_tuple = (19, "Ömer") # Tuple
person_dict = {'age': 19, 'name': 'Ömer'} # Dict
```

Ama dikkat ederseniz `dataclass`'lar kadar _kullanışlı_ olmadığını görürsünüz. Mesela `tuple`'de argumanların yerlerini karıştırabilirsiniz debug ederken bu işinizi çok zorlaştır. `dict` de ise dataya erişmek için mutlaka bir `key`'e ihtiyaç vardır.

```python
person_dict['name']  ## person_dict.name desek daha hoş olmaz mı?
```

> Aslında `dict` veri tipindeki objelerin verilerine nokta(.) notasyonu ile erişebiliriz. Şöyle:

> Konu ile ilgili içerik [Sözlük Veri Tipini Python Nesnesine Dönüştürme](https://www.coogger.com/@hakancelik96/sozluk-veri-tipini-python-nesnesine-donusturme/)

```python
class NDict(dict):
    def __init__(self, *arg, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)
        super().__init__(*arg, **kwargs)

person = NDict(name="Ömer", age=19)
person.age   # 19
person.name  # Ömer
```

Tabi bunun ne kadar zahmetli olduğunu görüyorsunuz. Ama durun yukarıdaki kodun daha iyisini yapan bir veri yapısı var zaten. `namedtuple`

```python
from collections import namedtuple

Person = namedtuple("Person", ['age', 'name'])
person = Person(16, "Ömer")

person
# Person(age=16, name='Ömer')
person.name
# Ömer
```

E `dataclass`'lardan farkı ne bunun?
Öncelikle `dataclass`'ların çok daha fazla özelliği buluyor. Yukarıda anlattığım _kalıtım_ ve _fonksiyon_ ekleme işlemleri `namedtuple`'de çok daha zor. Öte yandan karşılaştırma yaparken `namedtuple` istediğinizi vermeyecektir. Yukarıdaki örnekten devam edelim

```python
>>> person == (16, "Ömer")
True
```

İyi bir şey gibi gözükse de sonuçta kendi türünde olmadığını _zannettiğimiz_ objelerle tam anlamıyla doğru karşılaştırmalar vermiyor.

Ayırca `namedtuple` obje oluştuktan sonra verilerin değişmesine izin vermeyecektir.

```python
Person = namedtuple("Person", ['age', 'name'])
person = Person(16, "Ömer")
person.age = 22
----------------------------------------
AttributeError  Traceback (most recent call last)
<file> in <module>
      3 Person = namedtuple("Person", ['age', 'name'])
      4 person = Person(16, "Ömer")
----> 5 person.age = 22

AttributeError: can't set attribute
```

## field()

Bir seneryo üzerinden devam edelim.

```python
from dataclasses import dataclass
from typing import List

@dataclass
class Student:
    id: int
    name: str

@dataclass
class Lesson:
    students: List[Student]
```

Buradan yeni nesneler üretelim

```python
omer = Student(1, "Ömer")
bersu = Student(2, "Bersu")
math = Lesson([omer, bersu])
print(math)
# Lesson(students=[Student(id=1, name='Ömer'), Student(id=2, name='Bersu')])
```

Şimdi `Lesson` sınıfına default deger vermeyi deneyelim. Bunu yaparken bir factory fonksiyon yazalım.

```python
NAMES = ["Ömer", "Ahmet", "Cem", "Zehra", "Büşra", "Bersu"]

def collect_students():
    return [Student(i + 1, v) for i, v in enumerate(NAMES)]

collect_students()

# [Student(id=1, name='Ömer'),
# Student(id=2, name='Ahmet'),
# Student(id=3, name='Cem'),
# Student(id=4, name='Zehra'),
# Student(id=5, name='Büşra'),
# Student(id=6, name='Bersu')]
```

Teoride `Lesson`a varsayılan değer vermek için şöyle yaparsınız.

```python
@dataclass
class Lesson:
    students: List[Student] = collect_students()
```

Böyle bir tanım Python'ın en büyük anti-pattern'lerinden birisidir: Varsayılan olarak değişken değer kullanmak. Buradaki problem şu ki `Lesson`'nun tüm versiyonları aynı `.students`'in varsayılan liste objesini kullanacak. Kısacası bir `Lesson`'dan herhangi bir `Student` silindiği vakit `Lesson`'nun tüm versiyonlarından da silinecek. Aslına bakarsanız dataclass'lar bunun olmasının önüne geçip size ValueError döndürüyor.

```python
---------------------------------------------------
ValueError         Traceback (most recent call last)
<file> in <module>
     12     name: str
     13
---> 14 @dataclass
     15 class Lesson:
     16     students: List[Student] = collect_students()

\python\python37-32\lib\dataclasses.py in _get_field(cls, a_name, a_type)
    725     # For real fields, disallow mutable defaults for known types.
    726     if f._field_type is _FIELD and isinstance(f.default, (list, dict, set)):
--> 727         raise ValueError(f'mutable default {type(f.default)} for field '
    728                          f'{f.name} is not allowed: use default_factory')
    729

ValueError: mutable default <class 'list'> for field students is not allowed: use default_factory
```

Bunun önüne geçmek için `field` methodunun `default_factory` diye bir parametresi var.

```python
from datacasses import dataclass, field

@dataclass
class Lesson:
    students: List[Student] = field(default_factory=collect_students)

Lesson()
# Lesson(students=[Student(id=1, name='Ömer'), Student(id=2, name='Ahmet'), Student(id=3, name='Cem'), Student(id=4, name='Zehra'), Student(id=5, name='Büşra'), Student(id=6, name='Bersu')])
```

`field`, sadece `default_factory` ile sınırlı değil. [Bu bağlantıdan](https://docs.python.org/3/library/dataclasses.html#dataclasses.field) diğer parametrelere ve ne işe yaradıklarına ulaşabilirsiniz.

## Optimizasyon

Bahsedeceğim şey `__slots__`, `__slots__` kısaca, sınıflara dinamik olmayan _sabit_ attributelar belirleyerek RAM'dan ve _hızdan_ tasarruf sağlıyor. `__slots__`, kendi başına ele alınması gereken bir konu oluğu için [şuradan](https://medium.com/@mazlumagar/python-tricks-1-slots-e0c9b04f4c5a) daha fazla bilgiye ulaşabilirsiniz.

`dataclass`'larda `__slots__` kullanımı normal classlardaki gibidir.

```python
from dataclasses import dataclass, field

@dataclass
class NormalPerson:
    name: str
    age: int
    salary: int

@dataclass
class SlotPerson:
    __slots__ = ['name', 'age', 'salary']
    name: str
    age: int
    salary: int
```

Hafızada sahip olduğu büyüklüğe bakalım.

```python
from sys import getsizeof

getsizeof(NormalPerson("Ahmet", 33, 3000)), getsizeof(SlotPerson("Ahmet", 33, 3000))
# (32, 36)
```

Ayırca Python'un veriye erişmesi de normal class'lara göre daha hızlıdır.

```python
from timeit import timeit

timeit(setup="slot_p = SlotPerson('Ahmet', 33, 3000)", globals=globals())
# 0.012656699999752163
timeit(setup="normal_p = NormalPerson('Ahmet', 33, 3000)", globals=globals())
# 0.012095599999156548
```

tabi yazmış olduğumuz sınıfın basitliğinden dolayı aradaki fark oldukça az. Daha büyük sınıflarda bu fark dikkate değer biçimde artıyor.

---

## File: src/routes/blog/use-native-driver/index.md

---
title: useNativeDriver üzerine
date: 2021-11-01
tags:
  - React Native
  - Animation
---

Eğer Raact Native'de animasyon işleriyle uğraştıysanız, animasyonu çalıştırırken `useNativeDriver` parametresini görmüşsünüzdür. Bu paramereyi ilk gördüğümde düşündüğüm şey, "Sanırım animasyon native kısımda çalışacak" oldu. Evet gerçekten de `useNativeDriver` animasyonu native tarafda çalıştırıyor. Peki bunu _nasıl_ yapıyor?

Her şeyden önce bir şeyi açıklığa kavuşturalım. React Native, bütün ekranları native olarak oluşturur yani animasyonların da native olarak oluşturulması gerekir. Javascript bir kutunun yerinin değiştirirken değişikliği haber vermek için bir şekilde işletim sistemi ile haberleşmesi gerekir. Javascript ile Native kod farklı threadlarda çalıştığı için (UI Thread) değişikliğin React Native'in sağladığı "**bridge**" ile işletim sistemine aktarılması gerekir.

### Animasyonları JS'ye vermek

Bir animasyon javascript tarafında kabaca şu adımları takip ederek oluşturulur.

- Animasyon başlar
- JS `requestAnimationFrame` fonksiyonunu çalıştırır. (Bu fonksiyon her saniye 60 kere çalıştırılmaya çalışılır.)
- JS değiştirilmek istenen şeyin bir sonraki konumunu/opaklığını/transformunu hesaplar
- JS hesapladığı değişikliği `bridge` aracılığıyla işletim sistemine gönderir.
- Köprünün diğer ucunda bulunan native kod, (Android için Java, IOS için Obj-C) belirtilen transform işlemini uygular.
- Ekranda yeni değişiklik güncellenir.

Burada dikkat etmeniz gereken nokta şudur; Animasyon devam ederken React _re-render_ işlemi yapmaz. Bunun sebebi `Animated API`' nin değeri _doğrudan_ native tarafa göndermesidir. Eğer öyle olmasaydı, animasyon değeri bir _React state'i_ gibi olsaydı, 60 saniyede bir component render olacaktı. Bu çok ciddi bir *performans kaybı *olurdu.

Her şey güzel JS animasyonlarımızı güzel güzel oluşturuyor. Fakat...

JS **tek thread**'da çalışır yani animasyonun bir sonraki değeri hesaplanırken aynı anda başka bir işlem **yapamayız**. Bu da JS tarafında oluşturulan animasyonun "gecikmeli(_laggy_)" olmasına sebep olur.

Bir örnek bu gecikmeyi daha iyi anlamanıza yardımcı olabilir.

```jsx {11}
import React, { useRef } from 'react';
import { SafeAreaView, Animated, TouchableOpacity, Text } from 'react-native';

const App = () => {
	const scale = useRef(new Animated.Value(1)).current;

	const startAnimation = () => {
		Animated.timing(scale, {
			toValue: 2,
			duration: 600,
			useNativeDriver: false // Native dirver devre dışı
		}).start();
	};

	return (
		<SafeAreaView style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
			<Animated.View
				style={{
					height: 100,
					width: 100,
					backgroundColor: 'red',
					borderRadius: 9999,
					transform: [{ scale }]
				}}
			/>
			<TouchableOpacity onPress={startAnimation}>
				<Text>Press Me</Text>
			</TouchableOpacity>
		</SafeAreaView>
	);
};

export default App;
```



Evet JS güzel bir şekilde animasyonu çalıştırıyor. Biraz daha zorlayalım

```js {8-13}
const startAnimation = () => {
  Animated.timing(scale, {
    toValue: 2,
    duration: 600,
    useNativeDriver: false,
  }).start();

  setTimeout(() => {
    let r = 0
    for(let i = 0; i < 1000000000; i++) {
      r = i ** i
    }
  }, 10)
```



Görüldüğü üzere JS threadinda FPS düştü ve animasyon* akıcılığını kaybetti.* Bunun nedeni daha önce dediğimiz gibi `scale` değerinin diğer kodlarla birlikte JS threadinda çalışması ve diğer kodları beklemesi.

Biraz daha açarsak, animasyon başladığı anda `requestAnimationFrame`saniyede 60 kere çalıştırılmaya çalışılacak. JS her defasında bir işlemi yapabileceği için

```js
setTimeout(() => {
	let r = 0;
	for (let i = 0; i < 1000000000; i++) {
		r = i ** i;
	}
}, 10);
```

bu kod JS threadi _meşgul_ edecek bir bir sonraki animasyon karesi atlanacak. Sonuç olarak "smooth" olmayan animsayonlar oluşacak.

Neyse ki React Native, native tarafta animasyonları oluşturmamıza izin verir.

### Animasyonları native tarafta çalıştırmak

Yukardaki problemi basitçe `useNativeDriver` parametresini `true` yaparak çözebilirsiniz

```js {4}
Animated.timing(scale, {
	toValue: 2,
	duration: 600,
	useNativeDriver: true
}).start();
```



Peki bu nasıl oluyor? Kabaca şöyle;

- Animasyon başlar
- JS animasyon için gerekli olan önbilgiyi "bridge" aracılığıyla aktarır
- **Native tarafta** animasyon oluşturulur.

Bu şekilde animasyon hesaplamalarını JS threadinden ayırdık. Böylelikle hem daha **hızlı animasyonlar** yaptık hem de React Native Bridge'i _darboğaz_ yapmamış olduk.

Bu noktada aklınıza şu gelebilir; "E o zaman `useNativeDriver`'i her zaman açalım"

Animasyonları native tarafta çalıştırmanın bazi **eksileri** vardır.

- Animasyonlar üstünde daha az yetkiye sahip olursunuz. (JS, animasyon devam ederken olan biteni gözlemleyemez.)
- Daha az özeliği manipüle edebilirsiniz. Mesela `height` veya `width` özeliklerini native olarak animate edemezsiniz.

### Sonuç olarak

Animasyonların React Native'de nasıl çalıştığını, hangi aşamalardan geçtiğini, `useNativeDriver`'in ne olduğunu, ne işe yaradığını göstermeye çalıştım. Umarım paylaştığım bilgiler yararlı olur.

---

## File: .github/workflows/main.yaml

name: Fetch Bookmarks

on:
  push:
    branches:
      - master
  schedule:
    - cron: '0 */6 * * *'

jobs:
  build:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [10.x]

    steps:
      - name: 🍽️ Get working copy
        uses: actions/checkout@v2
        with:
          fetch-depth: 1

      - name: Set up node.js
        uses: actions/setup-node@v2
        with:
          node-version: '12'
      - name: Install deps
        run: yarn install --ignore-engines

      - name: Update bookmarks
        env:
          RAINDROP_API_KEY: ${{ secrets.RAINDROP_API_KEY }}
        run: yarn fetch:bookmarks

      - name: Deploy
        run: |
          git config user.name "bufgix"
          git config user.email "ooruc471@yandex.com"
          git add .
          git commit -am "feat(auto generate): Updated content"
          git push --all -f https://${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git