├── .gitignore
├── README.md
├── builds
├── flutter-release.aab
├── flutter-release.apk
├── kmp-release.aab
├── kmp-release.apk
├── native-release.aab
└── native-release.apk
├── docs
└── images
│ ├── chart_app_size.png
│ └── chart_startup_times.png
├── flutter
└── app
│ ├── .gitignore
│ ├── .metadata
│ ├── README.md
│ ├── analysis_options.yaml
│ ├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── app
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ │ ├── drawable-v21
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values-night
│ │ │ │ └── styles.xml
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ ├── macrobenchmark
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── macrobenchmark
│ │ │ └── ExampleStartupBenchmark.kt
│ └── settings.gradle
│ ├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ ├── Runner
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ │ ├── Icon-App-20x20@1x.png
│ │ │ │ ├── Icon-App-20x20@2x.png
│ │ │ │ ├── Icon-App-20x20@3x.png
│ │ │ │ ├── Icon-App-29x29@1x.png
│ │ │ │ ├── Icon-App-29x29@2x.png
│ │ │ │ ├── Icon-App-29x29@3x.png
│ │ │ │ ├── Icon-App-40x40@1x.png
│ │ │ │ ├── Icon-App-40x40@2x.png
│ │ │ │ ├── Icon-App-40x40@3x.png
│ │ │ │ ├── Icon-App-60x60@2x.png
│ │ │ │ ├── Icon-App-60x60@3x.png
│ │ │ │ ├── Icon-App-76x76@1x.png
│ │ │ │ ├── Icon-App-76x76@2x.png
│ │ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ │ └── LaunchImage.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── LaunchImage.png
│ │ │ │ ├── LaunchImage@2x.png
│ │ │ │ ├── LaunchImage@3x.png
│ │ │ │ └── README.md
│ │ ├── Base.lproj
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── Runner-Bridging-Header.h
│ └── RunnerTests
│ │ └── RunnerTests.swift
│ ├── lib
│ └── main.dart
│ ├── pubspec.lock
│ ├── pubspec.yaml
│ └── test
│ └── widget_test.dart
├── kmp
└── cats
│ ├── .gitignore
│ ├── app
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── cats
│ │ │ └── MainActivity.kt
│ │ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── backup_rules.xml
│ │ └── data_extraction_rules.xml
│ ├── build.gradle.kts
│ ├── gradle.properties
│ ├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── iosCats
│ ├── iosCats.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ ├── xcshareddata
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ │ └── xcuserdata
│ │ │ │ └── jacob.xcuserdatad
│ │ │ │ └── UserInterfaceState.xcuserstate
│ │ └── xcuserdata
│ │ │ └── jacob.xcuserdatad
│ │ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ └── iosCats
│ │ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ │ ├── ContentView.swift
│ │ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ │ └── iosCatsApp.swift
│ ├── macrobenchmark
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── example
│ │ └── macrobenchmark
│ │ └── ExampleStartupBenchmark.kt
│ ├── settings.gradle.kts
│ └── shared
│ ├── build.gradle.kts
│ └── src
│ ├── androidMain
│ └── kotlin
│ │ └── com
│ │ └── sample
│ │ └── cats
│ │ └── network
│ │ └── createKtorEngine.kt
│ ├── commonMain
│ └── kotlin
│ │ └── com
│ │ └── sample
│ │ └── cats
│ │ ├── data
│ │ ├── CatImageDto.kt
│ │ └── DefaultCatImagesRepository.kt
│ │ ├── domain
│ │ ├── CatImage.kt
│ │ └── CatImagesRepository.kt
│ │ ├── network
│ │ └── createKtorEngine.kt
│ │ └── ui
│ │ ├── CatImagesScreen.kt
│ │ ├── CatsApp.kt
│ │ └── theme
│ │ ├── CatImagesViewModel.kt
│ │ ├── Color.kt
│ │ ├── Theme.kt
│ │ └── Type.kt
│ └── iosMain
│ └── kotlin
│ ├── com
│ └── sample
│ │ └── cats
│ │ └── network
│ │ └── createKtorEngine.kt
│ └── main.ios.kt
├── native-android
└── cats
│ ├── .gitignore
│ ├── app
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── cats
│ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── cats
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── data
│ │ │ │ ├── CatImageDto.kt
│ │ │ │ └── DefaultCatImagesRepository.kt
│ │ │ │ ├── domain
│ │ │ │ ├── CatImage.kt
│ │ │ │ └── CatImagesRepository.kt
│ │ │ │ └── ui
│ │ │ │ ├── CatImagesScreen.kt
│ │ │ │ ├── CatsApp.kt
│ │ │ │ └── theme
│ │ │ │ ├── CatImagesViewModel.kt
│ │ │ │ ├── Color.kt
│ │ │ │ ├── Theme.kt
│ │ │ │ └── Type.kt
│ │ └── res
│ │ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── drawable
│ │ │ └── ic_launcher_background.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── themes.xml
│ │ │ └── xml
│ │ │ ├── backup_rules.xml
│ │ │ └── data_extraction_rules.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── example
│ │ └── cats
│ │ └── ExampleUnitTest.kt
│ ├── build.gradle.kts
│ ├── gradle.properties
│ ├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── macrobenchmark
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── example
│ │ └── macrobenchmark
│ │ └── ExampleStartupBenchmark.kt
│ └── settings.gradle.kts
└── native-ios
└── Cats
├── Cats.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ │ └── Package.resolved
│ └── xcuserdata
│ │ └── jacob.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ └── jacob.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
└── Cats
├── Assets.xcassets
├── AccentColor.colorset
│ └── Contents.json
├── AppIcon.appiconset
│ └── Contents.json
└── Contents.json
├── CatImage.swift
├── CatImagesViewModel.swift
├── CatsApp.swift
├── ContentView.swift
└── Preview Content
└── Preview Assets.xcassets
└── Contents.json
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | .idea
5 | .DS_Store
6 | /build
7 | */build
8 | /captures
9 | .externalNativeBuild
10 | .cxx
11 | local.properties
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flutter versus Native versus KMP: a Performance Comparison
2 |
3 | Comparing the performance of the same app in Flutter, native and Kotlin Multiplatform (KMP) on Android and iOS.
4 |
5 | ## 🐈 Sample app
6 |
7 | The app will contain a single screen, load cat pictures from a public API and show them in a horizontal list.
8 | Each picture can be clicked to show it zoomed in below the list.
9 |
10 | ## 📂 Technology overview
11 |
12 | | Folder | Technology | UI Framework | Network lib | Image lib |
13 | |-------------------|------------------------|--------------------------|-----------------------------------------------------|---------------------------------------------------------------|
14 | | `/flutter` | Cross-platform, Dart | Flutter | [http.dart](https://pub.dev/packages/http) | Built-in image widget |
15 | | `/kmp` | Cross-platform, Kotlin | Compose UI Multiplatform | [Ktor](https://github.com/ktorio/ktor) | [Kamel](https://github.com/Kamel-Media/Kamel) |
16 | | `/native-ios` | Native, Swift | SwiftUI | [Alamofire](https://github.com/Alamofire/Alamofire) | [AlamofireImage](https://github.com/Alamofire/AlamofireImage) |
17 | | `/native-android` | Native, Kotlin | Compose UI | [Ktor](https://github.com/ktorio/ktor) | [Kamel](https://github.com/Kamel-Media/Kamel) |
18 |
19 | ### 📦 Builds
20 |
21 | Release builds are available in `/builds`.
22 |
23 | ## 📊 Benchmark results
24 |
25 | ### 📦 App size
26 |
27 | Android sizes were measured by running Bundletool* on the signed, minified release bundles to create an APK set for a
28 | Pixel 4a, and then running `get-size` on that APK set. iOS sizes were measured running the App Thinning Size report.
29 |
30 | | Technology | Android | iOS | Remarks |
31 | |------------------------------|----------|----------------------------------|--------------------------------------------------------------|
32 | | **Native** (Compose/SwiftUI) | 1.463 MB | 1.7 MB | |
33 | | **KMP** (Compose) | 1.463 MB | 24.8 MB (v1.4)
32.2 MB (v1.5) | Includes Skia on iOS, where Android relies on built-in Skia. |
34 | | **Flutter** | 6.828 MB | 17.9 MB | Also includes Skia on iOS + Flutter framework. |
35 |
36 | Note: due to https://github.com/Kamel-Media/Kamel/issues/47 the Kamel library isn't being minified. Once that's fixed,
37 | the Android native and Android KMP builds will both be smaller.
38 |
39 | *commands used:
40 |
41 | * `bundletool build-apks --connected-device --bundle=release.aab --output=release.apks`
42 | * `bundletool get-size total --apks=release.apks`
43 |
44 | ### 🚀 Startup
45 |
46 | #### Android
47 |
48 | Android startup benchmarks were executed
49 | using [Macrobenchmark](https://developer.android.com/topic/performance/benchmarking/macrobenchmark-overview), 5 times on
50 | a Pixel 4a. Test suite was run twice
51 | in alternating order.
52 |
53 | | App | min. | median | max. |
54 | |---------------------|----------|----------|----------|
55 | | **Native** Android | 408.7 ms | 413.1 ms | 423.1 ms |
56 | | **KMP** Android | 403.6 ms | 425.3 ms | 466.4 ms |
57 | | **Flutter** Android | 600.5 ms | 634.2 ms | 649.8 ms |
58 |
59 | #### iOS
60 |
61 | The iOS startup tests were run on an iPhone 12 Mini.
62 |
63 | | App | Duration (AppLaunch) |
64 | |-----------------|----------------------|
65 | | **Native** iOS | 1.441 s |
66 | | **KMP** iOS | 1.618 s |
67 | | **Flutter** iOS | 1.608 s |
--------------------------------------------------------------------------------
/builds/flutter-release.aab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/builds/flutter-release.aab
--------------------------------------------------------------------------------
/builds/flutter-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/builds/flutter-release.apk
--------------------------------------------------------------------------------
/builds/kmp-release.aab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/builds/kmp-release.aab
--------------------------------------------------------------------------------
/builds/kmp-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/builds/kmp-release.apk
--------------------------------------------------------------------------------
/builds/native-release.aab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/builds/native-release.aab
--------------------------------------------------------------------------------
/builds/native-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/builds/native-release.apk
--------------------------------------------------------------------------------
/docs/images/chart_app_size.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/docs/images/chart_app_size.png
--------------------------------------------------------------------------------
/docs/images/chart_startup_times.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/docs/images/chart_startup_times.png
--------------------------------------------------------------------------------
/flutter/app/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | /build/
34 |
35 | # Symbolication related
36 | app.*.symbols
37 |
38 | # Obfuscation related
39 | app.*.map.json
40 |
41 | # Android Studio will place build artifacts here
42 | /android/app/debug
43 | /android/app/profile
44 | /android/app/release
45 |
--------------------------------------------------------------------------------
/flutter/app/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled.
5 |
6 | version:
7 | revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
8 | channel: stable
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
17 | base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
18 | - platform: android
19 | create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
20 | base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
21 | - platform: ios
22 | create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
23 | base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
24 |
25 | # User provided section
26 |
27 | # List of Local paths (relative to this file) that should be
28 | # ignored by the migrate tool.
29 | #
30 | # Files that are not part of the templates will be ignored by default.
31 | unmanaged_files:
32 | - 'lib/main.dart'
33 | - 'ios/Runner.xcodeproj/project.pbxproj'
34 |
--------------------------------------------------------------------------------
/flutter/app/README.md:
--------------------------------------------------------------------------------
1 | # app
2 |
3 | Cats.
--------------------------------------------------------------------------------
/flutter/app/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/flutter/app/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/flutter/app/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | namespace "com.example.app"
30 | compileSdkVersion flutter.compileSdkVersion
31 | ndkVersion flutter.ndkVersion
32 |
33 | compileOptions {
34 | sourceCompatibility JavaVersion.VERSION_1_8
35 | targetCompatibility JavaVersion.VERSION_1_8
36 | }
37 |
38 | kotlinOptions {
39 | jvmTarget = '1.8'
40 | }
41 |
42 | sourceSets {
43 | main.java.srcDirs += 'src/main/kotlin'
44 | }
45 |
46 | defaultConfig {
47 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
48 | applicationId "com.example.app"
49 | // You can update the following values to match your application needs.
50 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
51 | minSdkVersion flutter.minSdkVersion
52 | targetSdkVersion flutter.targetSdkVersion
53 | versionCode flutterVersionCode.toInteger()
54 | versionName flutterVersionName
55 | }
56 |
57 | buildTypes {
58 | release {
59 | minifyEnabled true
60 | shrinkResources true
61 | // TODO: Add your own signing config for the release build.
62 | // Signing with the debug keys for now, so `flutter run --release` works.
63 | signingConfig signingConfigs.debug
64 | }
65 | benchmark {
66 | initWith(buildTypes.getByName("release"))
67 | matchingFallbacks += ["release"]
68 | debuggable false
69 | }
70 | }
71 | }
72 |
73 | flutter {
74 | source '../..'
75 | }
76 |
77 | dependencies {
78 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
79 | }
80 |
--------------------------------------------------------------------------------
/flutter/app/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/flutter/app/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
17 |
21 |
22 |
26 |
27 |
28 |
29 |
30 |
31 |
33 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/flutter/app/android/app/src/main/kotlin/com/example/app/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.app
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/flutter/app/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/flutter/app/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/flutter/app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/flutter/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/flutter/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/flutter/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/flutter/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/flutter/app/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/flutter/app/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/flutter/app/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/flutter/app/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.7.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.3.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | tasks.register("clean", Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/flutter/app/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/flutter/app/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
6 |
--------------------------------------------------------------------------------
/flutter/app/android/macrobenchmark/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/flutter/app/android/macrobenchmark/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.test")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | android {
7 | namespace = "com.example.macrobenchmark"
8 | compileSdk = 33
9 |
10 | compileOptions {
11 | sourceCompatibility = JavaVersion.VERSION_1_8
12 | targetCompatibility = JavaVersion.VERSION_1_8
13 | }
14 |
15 | kotlinOptions {
16 | jvmTarget = "1.8"
17 | }
18 |
19 | defaultConfig {
20 | minSdk = 23
21 | targetSdk = 33
22 |
23 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
24 | }
25 |
26 | buildTypes {
27 | // This benchmark buildType is used for benchmarking, and should function like your
28 | // release build (for example, with minification on). It"s signed with a debug key
29 | // for easy local/CI testing.
30 | create("benchmark") {
31 | isDebuggable = true
32 | signingConfig = getByName("debug").signingConfig
33 | matchingFallbacks += listOf("release")
34 | }
35 | }
36 |
37 | targetProjectPath = ":app"
38 | experimentalProperties["android.experimental.self-instrumenting"] = true
39 | }
40 |
41 | dependencies {
42 | implementation("androidx.test.ext:junit:1.1.5")
43 | implementation("androidx.test.espresso:espresso-core:3.5.1")
44 | implementation("androidx.test.uiautomator:uiautomator:2.2.0")
45 | implementation("androidx.benchmark:benchmark-macro-junit4:1.1.1")
46 | }
47 |
48 | androidComponents {
49 | beforeVariants(selector().all()) {
50 | it.enable = it.buildType == "benchmark"
51 | }
52 | }
--------------------------------------------------------------------------------
/flutter/app/android/macrobenchmark/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/flutter/app/android/macrobenchmark/src/main/java/com/example/macrobenchmark/ExampleStartupBenchmark.kt:
--------------------------------------------------------------------------------
1 | package com.example.macrobenchmark
2 |
3 | import androidx.benchmark.macro.CompilationMode
4 | import androidx.benchmark.macro.StartupMode
5 | import androidx.benchmark.macro.StartupTimingMetric
6 | import androidx.benchmark.macro.junit4.MacrobenchmarkRule
7 | import androidx.test.ext.junit.runners.AndroidJUnit4
8 | import org.junit.Rule
9 | import org.junit.Test
10 | import org.junit.runner.RunWith
11 |
12 | /**
13 | * This is an example startup benchmark.
14 | *
15 | * It navigates to the device's home screen, and launches the default activity.
16 | *
17 | * Before running this benchmark:
18 | * 1) switch your app's active build variant in the Studio (affects Studio runs only)
19 | * 2) add `` to your app's manifest, within the `` tag
20 | *
21 | * Run this benchmark from Studio to see startup measurements, and captured system traces
22 | * for investigating your app's performance.
23 | */
24 | @RunWith(AndroidJUnit4::class)
25 | class ExampleStartupBenchmark {
26 | @get:Rule
27 | val benchmarkRule = MacrobenchmarkRule()
28 |
29 | @Test
30 | fun startup() = benchmarkRule.measureRepeated(
31 | packageName = "com.example.app",
32 | metrics = listOf(StartupTimingMetric()),
33 | iterations = 5,
34 | startupMode = StartupMode.COLD
35 | ) {
36 | pressHome()
37 | startActivityAndWait()
38 | }
39 | }
--------------------------------------------------------------------------------
/flutter/app/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | include ':macrobenchmark'
3 |
4 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
5 | def properties = new Properties()
6 |
7 | assert localPropertiesFile.exists()
8 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
9 |
10 | def flutterSdkPath = properties.getProperty("flutter.sdk")
11 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
12 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
13 |
--------------------------------------------------------------------------------
/flutter/app/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/flutter/app/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 11.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/flutter/app/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/flutter/app/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/flutter/app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/flutter/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/flutter/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/flutter/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
43 |
49 |
50 |
51 |
52 |
53 |
63 |
65 |
71 |
72 |
73 |
74 |
80 |
82 |
88 |
89 |
90 |
91 |
93 |
94 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/flutter/app/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/flutter/app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/flutter/app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/flutter/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | App
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | app
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(FLUTTER_BUILD_NUMBER)
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | UIViewControllerBasedStatusBarAppearance
45 |
46 | CADisableMinimumFrameDurationOnPhone
47 |
48 | UIApplicationSupportsIndirectInputEvents
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/flutter/app/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/flutter/app/ios/RunnerTests/RunnerTests.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 | import XCTest
4 |
5 | class RunnerTests: XCTestCase {
6 |
7 | func testExample() {
8 | // If you add code to the Runner application, consider adding tests here.
9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/flutter/app/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:http/http.dart' as http;
5 |
6 | void main() {
7 | runApp(const MyApp());
8 | }
9 |
10 | class CatImage {
11 | final String id;
12 | final String url;
13 |
14 | CatImage({required this.id, required this.url});
15 |
16 | factory CatImage.fromJson(Map json) {
17 | return CatImage(id: json['id'], url: json['url']);
18 | }
19 | }
20 |
21 | class MyApp extends StatelessWidget {
22 | const MyApp({super.key});
23 |
24 | // This widget is the root of your application.
25 | @override
26 | Widget build(BuildContext context) {
27 | return MaterialApp(
28 | title: 'Flutter Demo',
29 | theme: ThemeData(
30 | // This is the theme of your application.
31 | //
32 | // TRY THIS: Try running your application with "flutter run". You'll see
33 | // the application has a blue toolbar. Then, without quitting the app,
34 | // try changing the seedColor in the colorScheme below to Colors.green
35 | // and then invoke "hot reload" (save your changes or press the "hot
36 | // reload" button in a Flutter-supported IDE, or press "r" if you used
37 | // the command line to start the app).
38 | //
39 | // Notice that the counter didn't reset back to zero; the application
40 | // state is not lost during the reload. To reset the state, use hot
41 | // restart instead.
42 | //
43 | // This works for code too, not just values: Most code changes can be
44 | // tested with just a hot reload.
45 | colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
46 | useMaterial3: true,
47 | ),
48 | home: const MyHomePage(title: 'Cats'),
49 | );
50 | }
51 | }
52 |
53 | class MyHomePage extends StatefulWidget {
54 | const MyHomePage({super.key, required this.title});
55 |
56 | // This widget is the home page of your application. It is stateful, meaning
57 | // that it has a State object (defined below) that contains fields that affect
58 | // how it looks.
59 |
60 | // This class is the configuration for the state. It holds the values (in this
61 | // case the title) provided by the parent (in this case the App widget) and
62 | // used by the build method of the State. Fields in a Widget subclass are
63 | // always marked "final".
64 |
65 | final String title;
66 |
67 | @override
68 | State createState() => _MyHomePageState();
69 | }
70 |
71 | class _MyHomePageState extends State {
72 | List catImages = [];
73 | CatImage? selectedCatImage;
74 |
75 | @override
76 | void initState() {
77 | super.initState();
78 | fetchCatImages();
79 | }
80 |
81 | Future fetchCatImages() async {
82 | final response = await http
83 | .get(Uri.parse('https://api.thecatapi.com/v1/images/search?limit=10'));
84 |
85 | if (response.statusCode == 200) {
86 | List data = json.decode(response.body);
87 | List images =
88 | data.map((json) => CatImage.fromJson(json)).toList();
89 |
90 | setState(() {
91 | catImages = images;
92 | });
93 | } else {
94 | print('Failed to load images');
95 | }
96 | }
97 |
98 | void setSelectedImage(CatImage image) {
99 | setState(() {
100 | selectedCatImage = image;
101 | });
102 | }
103 |
104 | @override
105 | Widget build(BuildContext context) {
106 | // This method is rerun every time setState is called, for instance as done
107 | // by the _incrementCounter method above.
108 | //
109 | // The Flutter framework has been optimized to make rerunning build methods
110 | // fast, so that you can just rebuild anything that needs updating rather
111 | // than having to individually change instances of widgets.
112 | return Scaffold(
113 | appBar: AppBar(
114 | // TRY THIS: Try changing the color here to a specific color (to
115 | // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
116 | // change color while the other colors stay the same.
117 | backgroundColor: Theme.of(context).colorScheme.inversePrimary,
118 | // Here we take the value from the MyHomePage object that was created by
119 | // the App.build method, and use it to set our appbar title.
120 | title: Text(widget.title),
121 | ),
122 | body: Center(
123 | child: Column(
124 | mainAxisAlignment: MainAxisAlignment.center,
125 | children: [
126 | Container(
127 | height: 100, // Height of the horizontal list
128 | child: ListView.builder(
129 | scrollDirection: Axis.horizontal,
130 | itemCount: catImages.length,
131 | itemBuilder: (context, index) {
132 | return GestureDetector(
133 | onTap: () {
134 | setSelectedImage(catImages[index]);
135 | },
136 | child: Padding(
137 | padding: const EdgeInsets.all(8.0),
138 | child: Image.network(catImages[index].url),
139 | ),
140 | );
141 | },
142 | ),
143 | ),
144 | Expanded(
145 | child: selectedCatImage != null
146 | ? Center(
147 | child: Image.network(
148 | selectedCatImage!.url,
149 | height: 300,
150 | ),
151 | )
152 | : Container(),
153 | ),
154 | ],
155 | ),
156 | ),
157 | );
158 | }
159 | }
--------------------------------------------------------------------------------
/flutter/app/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "2.11.0"
12 | boolean_selector:
13 | dependency: transitive
14 | description:
15 | name: boolean_selector
16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.1.1"
20 | characters:
21 | dependency: transitive
22 | description:
23 | name: characters
24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "1.3.0"
28 | clock:
29 | dependency: transitive
30 | description:
31 | name: clock
32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "1.1.1"
36 | collection:
37 | dependency: transitive
38 | description:
39 | name: collection
40 | sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.17.1"
44 | cupertino_icons:
45 | dependency: "direct main"
46 | description:
47 | name: cupertino_icons
48 | sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.0.5"
52 | fake_async:
53 | dependency: transitive
54 | description:
55 | name: fake_async
56 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
57 | url: "https://pub.dev"
58 | source: hosted
59 | version: "1.3.1"
60 | flutter:
61 | dependency: "direct main"
62 | description: flutter
63 | source: sdk
64 | version: "0.0.0"
65 | flutter_lints:
66 | dependency: "direct dev"
67 | description:
68 | name: flutter_lints
69 | sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4"
70 | url: "https://pub.dev"
71 | source: hosted
72 | version: "2.0.2"
73 | flutter_test:
74 | dependency: "direct dev"
75 | description: flutter
76 | source: sdk
77 | version: "0.0.0"
78 | http:
79 | dependency: "direct main"
80 | description:
81 | name: http
82 | sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2"
83 | url: "https://pub.dev"
84 | source: hosted
85 | version: "0.13.6"
86 | http_parser:
87 | dependency: transitive
88 | description:
89 | name: http_parser
90 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
91 | url: "https://pub.dev"
92 | source: hosted
93 | version: "4.0.2"
94 | js:
95 | dependency: transitive
96 | description:
97 | name: js
98 | sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
99 | url: "https://pub.dev"
100 | source: hosted
101 | version: "0.6.7"
102 | lints:
103 | dependency: transitive
104 | description:
105 | name: lints
106 | sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
107 | url: "https://pub.dev"
108 | source: hosted
109 | version: "2.1.1"
110 | matcher:
111 | dependency: transitive
112 | description:
113 | name: matcher
114 | sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
115 | url: "https://pub.dev"
116 | source: hosted
117 | version: "0.12.15"
118 | material_color_utilities:
119 | dependency: transitive
120 | description:
121 | name: material_color_utilities
122 | sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
123 | url: "https://pub.dev"
124 | source: hosted
125 | version: "0.2.0"
126 | meta:
127 | dependency: transitive
128 | description:
129 | name: meta
130 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
131 | url: "https://pub.dev"
132 | source: hosted
133 | version: "1.9.1"
134 | path:
135 | dependency: transitive
136 | description:
137 | name: path
138 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
139 | url: "https://pub.dev"
140 | source: hosted
141 | version: "1.8.3"
142 | sky_engine:
143 | dependency: transitive
144 | description: flutter
145 | source: sdk
146 | version: "0.0.99"
147 | source_span:
148 | dependency: transitive
149 | description:
150 | name: source_span
151 | sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
152 | url: "https://pub.dev"
153 | source: hosted
154 | version: "1.9.1"
155 | stack_trace:
156 | dependency: transitive
157 | description:
158 | name: stack_trace
159 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
160 | url: "https://pub.dev"
161 | source: hosted
162 | version: "1.11.0"
163 | stream_channel:
164 | dependency: transitive
165 | description:
166 | name: stream_channel
167 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
168 | url: "https://pub.dev"
169 | source: hosted
170 | version: "2.1.1"
171 | string_scanner:
172 | dependency: transitive
173 | description:
174 | name: string_scanner
175 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
176 | url: "https://pub.dev"
177 | source: hosted
178 | version: "1.2.0"
179 | term_glyph:
180 | dependency: transitive
181 | description:
182 | name: term_glyph
183 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
184 | url: "https://pub.dev"
185 | source: hosted
186 | version: "1.2.1"
187 | test_api:
188 | dependency: transitive
189 | description:
190 | name: test_api
191 | sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
192 | url: "https://pub.dev"
193 | source: hosted
194 | version: "0.5.1"
195 | typed_data:
196 | dependency: transitive
197 | description:
198 | name: typed_data
199 | sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
200 | url: "https://pub.dev"
201 | source: hosted
202 | version: "1.3.2"
203 | vector_math:
204 | dependency: transitive
205 | description:
206 | name: vector_math
207 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
208 | url: "https://pub.dev"
209 | source: hosted
210 | version: "2.1.4"
211 | sdks:
212 | dart: ">=3.0.6 <4.0.0"
213 |
--------------------------------------------------------------------------------
/flutter/app/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: app
2 | description: Cats.
3 | # The following line prevents the package from being accidentally published to
4 | # pub.dev using `flutter pub publish`. This is preferred for private packages.
5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
6 |
7 | # The following defines the version and build number for your application.
8 | # A version number is three numbers separated by dots, like 1.2.43
9 | # followed by an optional build number separated by a +.
10 | # Both the version and the builder number may be overridden in flutter
11 | # build by specifying --build-name and --build-number, respectively.
12 | # In Android, build-name is used as versionName while build-number used as versionCode.
13 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
14 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
15 | # Read more about iOS versioning at
16 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
17 | # In Windows, build-name is used as the major, minor, and patch parts
18 | # of the product and file versions while build-number is used as the build suffix.
19 | version: 1.0.0+1
20 |
21 | environment:
22 | sdk: '>=3.0.6 <4.0.0'
23 |
24 | # Dependencies specify other packages that your package needs in order to work.
25 | # To automatically upgrade your package dependencies to the latest versions
26 | # consider running `flutter pub upgrade --major-versions`. Alternatively,
27 | # dependencies can be manually updated by changing the version numbers below to
28 | # the latest version available on pub.dev. To see which dependencies have newer
29 | # versions available, run `flutter pub outdated`.
30 | dependencies:
31 | flutter:
32 | sdk: flutter
33 | http: ^0.13.3
34 |
35 |
36 | # The following adds the Cupertino Icons font to your application.
37 | # Use with the CupertinoIcons class for iOS style icons.
38 | cupertino_icons: ^1.0.2
39 |
40 | dev_dependencies:
41 | flutter_test:
42 | sdk: flutter
43 |
44 | # The "flutter_lints" package below contains a set of recommended lints to
45 | # encourage good coding practices. The lint set provided by the package is
46 | # activated in the `analysis_options.yaml` file located at the root of your
47 | # package. See that file for information about deactivating specific lint
48 | # rules and activating additional ones.
49 | flutter_lints: ^2.0.0
50 |
51 | # For information on the generic Dart part of this file, see the
52 | # following page: https://dart.dev/tools/pub/pubspec
53 |
54 | # The following section is specific to Flutter packages.
55 | flutter:
56 |
57 | # The following line ensures that the Material Icons font is
58 | # included with your application, so that you can use the icons in
59 | # the material Icons class.
60 | uses-material-design: true
61 |
62 | # To add assets to your application, add an assets section, like this:
63 | # assets:
64 | # - images/a_dot_burr.jpeg
65 | # - images/a_dot_ham.jpeg
66 |
67 | # An image asset can refer to one or more resolution-specific "variants", see
68 | # https://flutter.dev/assets-and-images/#resolution-aware
69 |
70 | # For details regarding adding assets from package dependencies, see
71 | # https://flutter.dev/assets-and-images/#from-packages
72 |
73 | # To add custom fonts to your application, add a fonts section here,
74 | # in this "flutter" section. Each entry in this list should have a
75 | # "family" key with the font family name, and a "fonts" key with a
76 | # list giving the asset and other descriptors for the font. For
77 | # example:
78 | # fonts:
79 | # - family: Schyler
80 | # fonts:
81 | # - asset: fonts/Schyler-Regular.ttf
82 | # - asset: fonts/Schyler-Italic.ttf
83 | # style: italic
84 | # - family: Trajan Pro
85 | # fonts:
86 | # - asset: fonts/TrajanPro.ttf
87 | # - asset: fonts/TrajanPro_Bold.ttf
88 | # weight: 700
89 | #
90 | # For details regarding fonts from package dependencies,
91 | # see https://flutter.dev/custom-fonts/#from-packages
92 |
--------------------------------------------------------------------------------
/flutter/app/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility in the flutter_test package. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:app/main.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(const MyApp());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/kmp/cats/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 | build/
--------------------------------------------------------------------------------
/kmp/cats/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/kmp/cats/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("android")
3 | id("com.android.application")
4 | id("org.jetbrains.compose")
5 | }
6 |
7 | android {
8 | namespace = "com.example.cats"
9 | compileSdk = 34
10 |
11 | defaultConfig {
12 | applicationId = "com.example.cats"
13 | minSdk = 21
14 | targetSdk = 34
15 | versionCode = 1
16 | versionName = "1.0"
17 |
18 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
19 | vectorDrawables {
20 | useSupportLibrary = true
21 | }
22 | }
23 |
24 | buildTypes {
25 | release {
26 | isMinifyEnabled = true
27 | isShrinkResources = true
28 | signingConfig = signingConfigs.getByName("debug")
29 | proguardFiles(
30 | getDefaultProguardFile("proguard-android-optimize.txt"),
31 | "proguard-rules.pro"
32 | )
33 | }
34 | create("benchmark") {
35 | initWith(buildTypes.getByName("release"))
36 | matchingFallbacks += listOf("release")
37 | isDebuggable = false
38 | }
39 | }
40 | compileOptions {
41 | sourceCompatibility = JavaVersion.VERSION_17
42 | targetCompatibility = JavaVersion.VERSION_17
43 | }
44 | composeOptions {
45 | kotlinCompilerExtensionVersion = "1.5.3"
46 | }
47 | buildFeatures {
48 | compose = true
49 | }
50 | packaging {
51 | resources {
52 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
53 | }
54 | }
55 | }
56 |
57 | dependencies {
58 | implementation("androidx.core:core-ktx:1.10.1")
59 | implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
60 | implementation("androidx.activity:activity-compose:1.7.2")
61 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
62 |
63 | implementation(project(":shared"))
64 | }
--------------------------------------------------------------------------------
/kmp/cats/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 | -dontwarn org.slf4j.impl.StaticLoggerBinder
23 | -keep class io.kamel.** { *; }
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
17 |
18 |
19 |
20 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/java/com/example/cats/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.cats
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import com.sample.cats.ui.CatsApp
7 |
8 | class MainActivity : ComponentActivity() {
9 |
10 | override fun onCreate(savedInstanceState: Bundle?) {
11 | super.onCreate(savedInstanceState)
12 | setContent {
13 | CatsApp()
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
10 |
16 |
19 |
22 |
23 |
24 |
25 |
31 |
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
44 |
46 |
48 |
50 |
52 |
54 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
70 |
72 |
74 |
75 |
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/kmp/cats/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/kmp/cats/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/kmp/cats/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/kmp/cats/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/kmp/cats/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/kmp/cats/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/kmp/cats/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/kmp/cats/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/kmp/cats/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/kmp/cats/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Cats
3 |
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
--------------------------------------------------------------------------------
/kmp/cats/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
13 |
14 |
20 |
--------------------------------------------------------------------------------
/kmp/cats/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | id("com.android.application") version "8.1.0" apply false
4 | id("org.jetbrains.kotlin.android") version "1.9.10" apply false
5 | id("com.android.test") version "8.1.0" apply false
6 | id("org.jetbrains.compose") version "1.5.1" apply false
7 | }
--------------------------------------------------------------------------------
/kmp/cats/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
24 | org.jetbrains.compose.experimental.uikit.enabled=true
--------------------------------------------------------------------------------
/kmp/cats/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/kmp/cats/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/kmp/cats/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/kmp/cats/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
87 |
88 | # Use the maximum available, or set MAX_FD != -1 to use that value.
89 | MAX_FD=maximum
90 |
91 | warn () {
92 | echo "$*"
93 | } >&2
94 |
95 | die () {
96 | echo
97 | echo "$*"
98 | echo
99 | exit 1
100 | } >&2
101 |
102 | # OS specific support (must be 'true' or 'false').
103 | cygwin=false
104 | msys=false
105 | darwin=false
106 | nonstop=false
107 | case "$( uname )" in #(
108 | CYGWIN* ) cygwin=true ;; #(
109 | Darwin* ) darwin=true ;; #(
110 | MSYS* | MINGW* ) msys=true ;; #(
111 | NONSTOP* ) nonstop=true ;;
112 | esac
113 |
114 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
115 |
116 |
117 | # Determine the Java command to use to start the JVM.
118 | if [ -n "$JAVA_HOME" ] ; then
119 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
120 | # IBM's JDK on AIX uses strange locations for the executables
121 | JAVACMD=$JAVA_HOME/jre/sh/java
122 | else
123 | JAVACMD=$JAVA_HOME/bin/java
124 | fi
125 | if [ ! -x "$JAVACMD" ] ; then
126 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
127 |
128 | Please set the JAVA_HOME variable in your environment to match the
129 | location of your Java installation."
130 | fi
131 | else
132 | JAVACMD=java
133 | if ! command -v java >/dev/null 2>&1
134 | then
135 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
136 |
137 | Please set the JAVA_HOME variable in your environment to match the
138 | location of your Java installation."
139 | fi
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
147 | # shellcheck disable=SC3045
148 | MAX_FD=$( ulimit -H -n ) ||
149 | warn "Could not query maximum file descriptor limit"
150 | esac
151 | case $MAX_FD in #(
152 | '' | soft) :;; #(
153 | *)
154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
155 | # shellcheck disable=SC3045
156 | ulimit -n "$MAX_FD" ||
157 | warn "Could not set maximum file descriptor limit to $MAX_FD"
158 | esac
159 | fi
160 |
161 | # Collect all arguments for the java command, stacking in reverse order:
162 | # * args from the command line
163 | # * the main class name
164 | # * -classpath
165 | # * -D...appname settings
166 | # * --module-path (only if needed)
167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
168 |
169 | # For Cygwin or MSYS, switch paths to Windows format before running java
170 | if "$cygwin" || "$msys" ; then
171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
173 |
174 | JAVACMD=$( cygpath --unix "$JAVACMD" )
175 |
176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
177 | for arg do
178 | if
179 | case $arg in #(
180 | -*) false ;; # don't mess with options #(
181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
182 | [ -e "$t" ] ;; #(
183 | *) false ;;
184 | esac
185 | then
186 | arg=$( cygpath --path --ignore --mixed "$arg" )
187 | fi
188 | # Roll the args list around exactly as many times as the number of
189 | # args, so each arg winds up back in the position where it started, but
190 | # possibly modified.
191 | #
192 | # NB: a `for` loop captures its iteration list before it begins, so
193 | # changing the positional parameters here affects neither the number of
194 | # iterations, nor the values presented in `arg`.
195 | shift # remove old arg
196 | set -- "$@" "$arg" # push replacement arg
197 | done
198 | fi
199 |
200 |
201 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
202 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
203 |
204 | # Collect all arguments for the java command;
205 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
206 | # shell script including quotes and variable substitutions, so put them in
207 | # double quotes to make sure that they get re-expanded; and
208 | # * put everything else in single quotes, so that it's not re-expanded.
209 |
210 | set -- \
211 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
212 | -classpath "$CLASSPATH" \
213 | org.gradle.wrapper.GradleWrapperMain \
214 | "$@"
215 |
216 | # Stop when "xargs" is not available.
217 | if ! command -v xargs >/dev/null 2>&1
218 | then
219 | die "xargs is not available"
220 | fi
221 |
222 | # Use "xargs" to parse quoted args.
223 | #
224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
225 | #
226 | # In Bash we could simply go:
227 | #
228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
229 | # set -- "${ARGS[@]}" "$@"
230 | #
231 | # but POSIX shell has neither arrays nor command substitution, so instead we
232 | # post-process each arg (as a line of input to sed) to backslash-escape any
233 | # character that might be a shell metacharacter, then use eval to reverse
234 | # that process (while maintaining the separation between arguments), and wrap
235 | # the whole thing up as a single "set" statement.
236 | #
237 | # This will of course break if any of these variables contains a newline or
238 | # an unmatched quote.
239 | #
240 |
241 | eval "set -- $(
242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
243 | xargs -n1 |
244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
245 | tr '\n' ' '
246 | )" '"$@"'
247 |
248 | exec "$JAVACMD" "$@"
249 |
--------------------------------------------------------------------------------
/kmp/cats/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/kmp/cats/iosCats/iosCats.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1623BE262A7D116A0089EC7C /* iosCatsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1623BE252A7D116A0089EC7C /* iosCatsApp.swift */; };
11 | 1623BE282A7D116A0089EC7C /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1623BE272A7D116A0089EC7C /* ContentView.swift */; };
12 | 1623BE2A2A7D116C0089EC7C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1623BE292A7D116C0089EC7C /* Assets.xcassets */; };
13 | 1623BE2D2A7D116C0089EC7C /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1623BE2C2A7D116C0089EC7C /* Preview Assets.xcassets */; };
14 | /* End PBXBuildFile section */
15 |
16 | /* Begin PBXFileReference section */
17 | 1623BE222A7D116A0089EC7C /* iosCats.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosCats.app; sourceTree = BUILT_PRODUCTS_DIR; };
18 | 1623BE252A7D116A0089EC7C /* iosCatsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iosCatsApp.swift; sourceTree = ""; };
19 | 1623BE272A7D116A0089EC7C /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
20 | 1623BE292A7D116C0089EC7C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
21 | 1623BE2C2A7D116C0089EC7C /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
22 | /* End PBXFileReference section */
23 |
24 | /* Begin PBXFrameworksBuildPhase section */
25 | 1623BE1F2A7D116A0089EC7C /* Frameworks */ = {
26 | isa = PBXFrameworksBuildPhase;
27 | buildActionMask = 2147483647;
28 | files = (
29 | );
30 | runOnlyForDeploymentPostprocessing = 0;
31 | };
32 | /* End PBXFrameworksBuildPhase section */
33 |
34 | /* Begin PBXGroup section */
35 | 1623BE192A7D116A0089EC7C = {
36 | isa = PBXGroup;
37 | children = (
38 | 1623BE242A7D116A0089EC7C /* iosCats */,
39 | 1623BE232A7D116A0089EC7C /* Products */,
40 | );
41 | sourceTree = "";
42 | };
43 | 1623BE232A7D116A0089EC7C /* Products */ = {
44 | isa = PBXGroup;
45 | children = (
46 | 1623BE222A7D116A0089EC7C /* iosCats.app */,
47 | );
48 | name = Products;
49 | sourceTree = "";
50 | };
51 | 1623BE242A7D116A0089EC7C /* iosCats */ = {
52 | isa = PBXGroup;
53 | children = (
54 | 1623BE252A7D116A0089EC7C /* iosCatsApp.swift */,
55 | 1623BE272A7D116A0089EC7C /* ContentView.swift */,
56 | 1623BE292A7D116C0089EC7C /* Assets.xcassets */,
57 | 1623BE2B2A7D116C0089EC7C /* Preview Content */,
58 | );
59 | path = iosCats;
60 | sourceTree = "";
61 | };
62 | 1623BE2B2A7D116C0089EC7C /* Preview Content */ = {
63 | isa = PBXGroup;
64 | children = (
65 | 1623BE2C2A7D116C0089EC7C /* Preview Assets.xcassets */,
66 | );
67 | path = "Preview Content";
68 | sourceTree = "";
69 | };
70 | /* End PBXGroup section */
71 |
72 | /* Begin PBXNativeTarget section */
73 | 1623BE212A7D116A0089EC7C /* iosCats */ = {
74 | isa = PBXNativeTarget;
75 | buildConfigurationList = 1623BE302A7D116C0089EC7C /* Build configuration list for PBXNativeTarget "iosCats" */;
76 | buildPhases = (
77 | 1623BE332A7D11910089EC7C /* ShellScript */,
78 | 1623BE1E2A7D116A0089EC7C /* Sources */,
79 | 1623BE1F2A7D116A0089EC7C /* Frameworks */,
80 | 1623BE202A7D116A0089EC7C /* Resources */,
81 | );
82 | buildRules = (
83 | );
84 | dependencies = (
85 | );
86 | name = iosCats;
87 | productName = iosCats;
88 | productReference = 1623BE222A7D116A0089EC7C /* iosCats.app */;
89 | productType = "com.apple.product-type.application";
90 | };
91 | /* End PBXNativeTarget section */
92 |
93 | /* Begin PBXProject section */
94 | 1623BE1A2A7D116A0089EC7C /* Project object */ = {
95 | isa = PBXProject;
96 | attributes = {
97 | BuildIndependentTargetsInParallel = 1;
98 | LastSwiftUpdateCheck = 1430;
99 | LastUpgradeCheck = 1430;
100 | TargetAttributes = {
101 | 1623BE212A7D116A0089EC7C = {
102 | CreatedOnToolsVersion = 14.3.1;
103 | };
104 | };
105 | };
106 | buildConfigurationList = 1623BE1D2A7D116A0089EC7C /* Build configuration list for PBXProject "iosCats" */;
107 | compatibilityVersion = "Xcode 14.0";
108 | developmentRegion = en;
109 | hasScannedForEncodings = 0;
110 | knownRegions = (
111 | en,
112 | Base,
113 | );
114 | mainGroup = 1623BE192A7D116A0089EC7C;
115 | productRefGroup = 1623BE232A7D116A0089EC7C /* Products */;
116 | projectDirPath = "";
117 | projectRoot = "";
118 | targets = (
119 | 1623BE212A7D116A0089EC7C /* iosCats */,
120 | );
121 | };
122 | /* End PBXProject section */
123 |
124 | /* Begin PBXResourcesBuildPhase section */
125 | 1623BE202A7D116A0089EC7C /* Resources */ = {
126 | isa = PBXResourcesBuildPhase;
127 | buildActionMask = 2147483647;
128 | files = (
129 | 1623BE2D2A7D116C0089EC7C /* Preview Assets.xcassets in Resources */,
130 | 1623BE2A2A7D116C0089EC7C /* Assets.xcassets in Resources */,
131 | );
132 | runOnlyForDeploymentPostprocessing = 0;
133 | };
134 | /* End PBXResourcesBuildPhase section */
135 |
136 | /* Begin PBXShellScriptBuildPhase section */
137 | 1623BE332A7D11910089EC7C /* ShellScript */ = {
138 | isa = PBXShellScriptBuildPhase;
139 | buildActionMask = 2147483647;
140 | files = (
141 | );
142 | inputFileListPaths = (
143 | );
144 | inputPaths = (
145 | );
146 | outputFileListPaths = (
147 | );
148 | outputPaths = (
149 | );
150 | runOnlyForDeploymentPostprocessing = 0;
151 | shellPath = /bin/sh;
152 | shellScript = "cd \"$SRCROOT/..\"\n./gradlew :shared:embedAndSignAppleFrameworkForXcode\n";
153 | };
154 | /* End PBXShellScriptBuildPhase section */
155 |
156 | /* Begin PBXSourcesBuildPhase section */
157 | 1623BE1E2A7D116A0089EC7C /* Sources */ = {
158 | isa = PBXSourcesBuildPhase;
159 | buildActionMask = 2147483647;
160 | files = (
161 | 1623BE282A7D116A0089EC7C /* ContentView.swift in Sources */,
162 | 1623BE262A7D116A0089EC7C /* iosCatsApp.swift in Sources */,
163 | );
164 | runOnlyForDeploymentPostprocessing = 0;
165 | };
166 | /* End PBXSourcesBuildPhase section */
167 |
168 | /* Begin XCBuildConfiguration section */
169 | 1623BE2E2A7D116C0089EC7C /* Debug */ = {
170 | isa = XCBuildConfiguration;
171 | buildSettings = {
172 | ALWAYS_SEARCH_USER_PATHS = NO;
173 | CLANG_ANALYZER_NONNULL = YES;
174 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
175 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
176 | CLANG_ENABLE_MODULES = YES;
177 | CLANG_ENABLE_OBJC_ARC = YES;
178 | CLANG_ENABLE_OBJC_WEAK = YES;
179 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
180 | CLANG_WARN_BOOL_CONVERSION = YES;
181 | CLANG_WARN_COMMA = YES;
182 | CLANG_WARN_CONSTANT_CONVERSION = YES;
183 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
184 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
185 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
186 | CLANG_WARN_EMPTY_BODY = YES;
187 | CLANG_WARN_ENUM_CONVERSION = YES;
188 | CLANG_WARN_INFINITE_RECURSION = YES;
189 | CLANG_WARN_INT_CONVERSION = YES;
190 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
191 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
192 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
193 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
194 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
195 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
196 | CLANG_WARN_STRICT_PROTOTYPES = YES;
197 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
198 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
199 | CLANG_WARN_UNREACHABLE_CODE = YES;
200 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
201 | COPY_PHASE_STRIP = NO;
202 | DEBUG_INFORMATION_FORMAT = dwarf;
203 | ENABLE_STRICT_OBJC_MSGSEND = YES;
204 | ENABLE_TESTABILITY = YES;
205 | GCC_C_LANGUAGE_STANDARD = gnu11;
206 | GCC_DYNAMIC_NO_PIC = NO;
207 | GCC_NO_COMMON_BLOCKS = YES;
208 | GCC_OPTIMIZATION_LEVEL = 0;
209 | GCC_PREPROCESSOR_DEFINITIONS = (
210 | "DEBUG=1",
211 | "$(inherited)",
212 | );
213 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
214 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
215 | GCC_WARN_UNDECLARED_SELECTOR = YES;
216 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
217 | GCC_WARN_UNUSED_FUNCTION = YES;
218 | GCC_WARN_UNUSED_VARIABLE = YES;
219 | IPHONEOS_DEPLOYMENT_TARGET = 16.4;
220 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
221 | MTL_FAST_MATH = YES;
222 | ONLY_ACTIVE_ARCH = YES;
223 | SDKROOT = iphoneos;
224 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
225 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
226 | };
227 | name = Debug;
228 | };
229 | 1623BE2F2A7D116C0089EC7C /* Release */ = {
230 | isa = XCBuildConfiguration;
231 | buildSettings = {
232 | ALWAYS_SEARCH_USER_PATHS = NO;
233 | CLANG_ANALYZER_NONNULL = YES;
234 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
235 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
236 | CLANG_ENABLE_MODULES = YES;
237 | CLANG_ENABLE_OBJC_ARC = YES;
238 | CLANG_ENABLE_OBJC_WEAK = YES;
239 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
240 | CLANG_WARN_BOOL_CONVERSION = YES;
241 | CLANG_WARN_COMMA = YES;
242 | CLANG_WARN_CONSTANT_CONVERSION = YES;
243 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
244 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
245 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
246 | CLANG_WARN_EMPTY_BODY = YES;
247 | CLANG_WARN_ENUM_CONVERSION = YES;
248 | CLANG_WARN_INFINITE_RECURSION = YES;
249 | CLANG_WARN_INT_CONVERSION = YES;
250 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
251 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
252 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
253 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
254 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
255 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
256 | CLANG_WARN_STRICT_PROTOTYPES = YES;
257 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
258 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
259 | CLANG_WARN_UNREACHABLE_CODE = YES;
260 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
261 | COPY_PHASE_STRIP = NO;
262 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
263 | ENABLE_NS_ASSERTIONS = NO;
264 | ENABLE_STRICT_OBJC_MSGSEND = YES;
265 | GCC_C_LANGUAGE_STANDARD = gnu11;
266 | GCC_NO_COMMON_BLOCKS = YES;
267 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
268 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
269 | GCC_WARN_UNDECLARED_SELECTOR = YES;
270 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
271 | GCC_WARN_UNUSED_FUNCTION = YES;
272 | GCC_WARN_UNUSED_VARIABLE = YES;
273 | IPHONEOS_DEPLOYMENT_TARGET = 16.4;
274 | MTL_ENABLE_DEBUG_INFO = NO;
275 | MTL_FAST_MATH = YES;
276 | SDKROOT = iphoneos;
277 | SWIFT_COMPILATION_MODE = wholemodule;
278 | SWIFT_OPTIMIZATION_LEVEL = "-O";
279 | VALIDATE_PRODUCT = YES;
280 | };
281 | name = Release;
282 | };
283 | 1623BE312A7D116C0089EC7C /* Debug */ = {
284 | isa = XCBuildConfiguration;
285 | buildSettings = {
286 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
287 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
288 | CODE_SIGN_STYLE = Automatic;
289 | CURRENT_PROJECT_VERSION = 1;
290 | DEVELOPMENT_ASSET_PATHS = "\"iosCats/Preview Content\"";
291 | ENABLE_PREVIEWS = YES;
292 | FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)";
293 | GENERATE_INFOPLIST_FILE = YES;
294 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
295 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
296 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
297 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
298 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
299 | LD_RUNPATH_SEARCH_PATHS = (
300 | "$(inherited)",
301 | "@executable_path/Frameworks",
302 | );
303 | MARKETING_VERSION = 1.0;
304 | OTHER_LDFLAGS = (
305 | "$(inherited)",
306 | "-framework",
307 | shared,
308 | );
309 | PRODUCT_BUNDLE_IDENTIFIER = com.example.iosCats;
310 | PRODUCT_NAME = "$(TARGET_NAME)";
311 | SWIFT_EMIT_LOC_STRINGS = YES;
312 | SWIFT_VERSION = 5.0;
313 | TARGETED_DEVICE_FAMILY = "1,2";
314 | };
315 | name = Debug;
316 | };
317 | 1623BE322A7D116C0089EC7C /* Release */ = {
318 | isa = XCBuildConfiguration;
319 | buildSettings = {
320 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
321 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
322 | CODE_SIGN_STYLE = Automatic;
323 | CURRENT_PROJECT_VERSION = 1;
324 | DEVELOPMENT_ASSET_PATHS = "\"iosCats/Preview Content\"";
325 | ENABLE_PREVIEWS = YES;
326 | FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)";
327 | GENERATE_INFOPLIST_FILE = YES;
328 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
329 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
330 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
331 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
332 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
333 | LD_RUNPATH_SEARCH_PATHS = (
334 | "$(inherited)",
335 | "@executable_path/Frameworks",
336 | );
337 | MARKETING_VERSION = 1.0;
338 | OTHER_LDFLAGS = (
339 | "$(inherited)",
340 | "-framework",
341 | shared,
342 | );
343 | PRODUCT_BUNDLE_IDENTIFIER = com.example.iosCats;
344 | PRODUCT_NAME = "$(TARGET_NAME)";
345 | SWIFT_EMIT_LOC_STRINGS = YES;
346 | SWIFT_VERSION = 5.0;
347 | TARGETED_DEVICE_FAMILY = "1,2";
348 | };
349 | name = Release;
350 | };
351 | /* End XCBuildConfiguration section */
352 |
353 | /* Begin XCConfigurationList section */
354 | 1623BE1D2A7D116A0089EC7C /* Build configuration list for PBXProject "iosCats" */ = {
355 | isa = XCConfigurationList;
356 | buildConfigurations = (
357 | 1623BE2E2A7D116C0089EC7C /* Debug */,
358 | 1623BE2F2A7D116C0089EC7C /* Release */,
359 | );
360 | defaultConfigurationIsVisible = 0;
361 | defaultConfigurationName = Release;
362 | };
363 | 1623BE302A7D116C0089EC7C /* Build configuration list for PBXNativeTarget "iosCats" */ = {
364 | isa = XCConfigurationList;
365 | buildConfigurations = (
366 | 1623BE312A7D116C0089EC7C /* Debug */,
367 | 1623BE322A7D116C0089EC7C /* Release */,
368 | );
369 | defaultConfigurationIsVisible = 0;
370 | defaultConfigurationName = Release;
371 | };
372 | /* End XCConfigurationList section */
373 | };
374 | rootObject = 1623BE1A2A7D116A0089EC7C /* Project object */;
375 | }
376 |
--------------------------------------------------------------------------------
/kmp/cats/iosCats/iosCats.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/kmp/cats/iosCats/iosCats.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/kmp/cats/iosCats/iosCats.xcodeproj/project.xcworkspace/xcuserdata/jacob.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/kmp/cats/iosCats/iosCats.xcodeproj/project.xcworkspace/xcuserdata/jacob.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/kmp/cats/iosCats/iosCats.xcodeproj/xcuserdata/jacob.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | iosCats.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/kmp/cats/iosCats/iosCats/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/kmp/cats/iosCats/iosCats/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/kmp/cats/iosCats/iosCats/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/kmp/cats/iosCats/iosCats/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // iosCats
4 | //
5 | // Created by Jacob on 04.08.2023.
6 | //
7 |
8 | import SwiftUI
9 | import shared
10 |
11 | struct ComposeView: UIViewControllerRepresentable {
12 | func makeUIViewController(context: Context) -> UIViewController {
13 | Main_iosKt.MainViewController()
14 | }
15 |
16 | func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
17 | }
18 |
19 | struct ContentView: View {
20 | var body: some View {
21 | ComposeView()
22 | .ignoresSafeArea(.all, edges: .bottom)
23 | }
24 | }
25 |
26 | struct ContentView_Previews: PreviewProvider {
27 | static var previews: some View {
28 | ContentView()
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/kmp/cats/iosCats/iosCats/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/kmp/cats/iosCats/iosCats/iosCatsApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // iosCatsApp.swift
3 | // iosCats
4 | //
5 | // Created by Jacob on 04.08.2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct iosCatsApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/kmp/cats/macrobenchmark/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/kmp/cats/macrobenchmark/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("android")
3 | id("com.android.test")
4 | }
5 |
6 | android {
7 | namespace = "com.example.macrobenchmark"
8 | compileSdk = 34
9 |
10 | compileOptions {
11 | sourceCompatibility = JavaVersion.VERSION_1_8
12 | targetCompatibility = JavaVersion.VERSION_1_8
13 | }
14 |
15 | kotlinOptions {
16 | jvmTarget = "1.8"
17 | }
18 |
19 | defaultConfig {
20 | minSdk = 23
21 | targetSdk = 34
22 |
23 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
24 | }
25 |
26 | buildTypes {
27 | // This benchmark buildType is used for benchmarking, and should function like your
28 | // release build (for example, with minification on). It"s signed with a debug key
29 | // for easy local/CI testing.
30 | create("benchmark") {
31 | isDebuggable = true
32 | signingConfig = getByName("debug").signingConfig
33 | matchingFallbacks += listOf("release")
34 | }
35 | }
36 |
37 | targetProjectPath = ":app"
38 | experimentalProperties["android.experimental.self-instrumenting"] = true
39 | }
40 |
41 | dependencies {
42 | implementation("androidx.test.ext:junit:1.1.5")
43 | implementation("androidx.test.espresso:espresso-core:3.5.1")
44 | implementation("androidx.test.uiautomator:uiautomator:2.2.0")
45 | implementation("androidx.benchmark:benchmark-macro-junit4:1.1.1")
46 | }
47 |
48 | androidComponents {
49 | beforeVariants(selector().all()) {
50 | it.enable = it.buildType == "benchmark"
51 | }
52 | }
--------------------------------------------------------------------------------
/kmp/cats/macrobenchmark/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/kmp/cats/macrobenchmark/src/main/java/com/example/macrobenchmark/ExampleStartupBenchmark.kt:
--------------------------------------------------------------------------------
1 | package com.example.macrobenchmark
2 |
3 | import androidx.benchmark.macro.CompilationMode
4 | import androidx.benchmark.macro.StartupMode
5 | import androidx.benchmark.macro.StartupTimingMetric
6 | import androidx.benchmark.macro.junit4.MacrobenchmarkRule
7 | import androidx.test.ext.junit.runners.AndroidJUnit4
8 | import org.junit.Rule
9 | import org.junit.Test
10 | import org.junit.runner.RunWith
11 |
12 | /**
13 | * This is an example startup benchmark.
14 | *
15 | * It navigates to the device's home screen, and launches the default activity.
16 | *
17 | * Before running this benchmark:
18 | * 1) switch your app's active build variant in the Studio (affects Studio runs only)
19 | * 2) add `` to your app's manifest, within the `` tag
20 | *
21 | * Run this benchmark from Studio to see startup measurements, and captured system traces
22 | * for investigating your app's performance.
23 | */
24 | @RunWith(AndroidJUnit4::class)
25 | class ExampleStartupBenchmark {
26 | @get:Rule
27 | val benchmarkRule = MacrobenchmarkRule()
28 |
29 | @Test
30 | fun startup() = benchmarkRule.measureRepeated(
31 | packageName = "com.example.cats",
32 | metrics = listOf(StartupTimingMetric()),
33 | iterations = 5,
34 | startupMode = StartupMode.COLD
35 | ) {
36 | pressHome()
37 | startActivityAndWait()
38 | }
39 | }
--------------------------------------------------------------------------------
/kmp/cats/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | }
7 | plugins {
8 | kotlin("multiplatform") version "1.9.10"
9 | id("org.jetbrains.kotlin.plugin.serialization") version "1.9.10"
10 | }
11 | }
12 |
13 | @Suppress("UnstableApiUsage")
14 | dependencyResolutionManagement {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.name = "Cats"
22 | include(":app")
23 | include(":shared")
24 | include(":macrobenchmark")
--------------------------------------------------------------------------------
/kmp/cats/shared/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("multiplatform")
3 | kotlin("plugin.serialization")
4 | id("com.android.library")
5 | id("org.jetbrains.compose")
6 | }
7 |
8 | android {
9 | namespace = "com.example.cats.shared"
10 | compileSdk = 34
11 |
12 | compileOptions {
13 | sourceCompatibility = JavaVersion.VERSION_17
14 | targetCompatibility = JavaVersion.VERSION_17
15 | }
16 | defaultConfig {
17 | minSdk = 21
18 | }
19 | composeOptions {
20 | kotlinCompilerExtensionVersion = "1.5.3"
21 | }
22 | }
23 |
24 | @Suppress("OPT_IN_USAGE")
25 | kotlin {
26 | targetHierarchy.default()
27 |
28 | androidTarget()
29 | iosArm64()
30 | iosSimulatorArm64()
31 |
32 | sourceSets {
33 | listOf(iosX64(), iosArm64(), iosSimulatorArm64()).forEach {
34 | it.binaries.framework {
35 | baseName = "shared"
36 | }
37 | }
38 |
39 | val commonMain by getting {
40 | dependencies {
41 | implementation(compose.foundation)
42 | implementation(compose.material3)
43 | implementation(compose.ui)
44 |
45 | implementation("io.ktor:ktor-client-core:2.3.3")
46 | implementation("media.kamel:kamel-image:0.7.0")
47 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
48 | implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
49 | }
50 | }
51 | val androidMain by getting {
52 | dependencies {
53 | implementation("io.ktor:ktor-client-android:2.3.3")
54 | }
55 | }
56 | val iosMain by getting {
57 | dependencies {
58 | implementation("io.ktor:ktor-client-darwin:2.3.3")
59 | }
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/kmp/cats/shared/src/androidMain/kotlin/com/sample/cats/network/createKtorEngine.kt:
--------------------------------------------------------------------------------
1 | package com.sample.cats.network
2 |
3 | import io.ktor.client.engine.HttpClientEngineFactory
4 | import io.ktor.client.engine.android.Android
5 |
6 | actual fun createKtorEngine(): HttpClientEngineFactory<*> = Android
--------------------------------------------------------------------------------
/kmp/cats/shared/src/commonMain/kotlin/com/sample/cats/data/CatImageDto.kt:
--------------------------------------------------------------------------------
1 | package com.sample.cats.data
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | internal data class CatImageDto(
8 | @SerialName("id")
9 | val id: String,
10 |
11 | @SerialName("url")
12 | val url: String
13 | )
--------------------------------------------------------------------------------
/kmp/cats/shared/src/commonMain/kotlin/com/sample/cats/data/DefaultCatImagesRepository.kt:
--------------------------------------------------------------------------------
1 | package com.sample.cats.data
2 |
3 | import com.sample.cats.domain.CatImage
4 | import com.sample.cats.domain.CatImagesRepository
5 | import com.sample.cats.network.createKtorEngine
6 | import io.ktor.client.HttpClient
7 | import io.ktor.client.request.get
8 | import io.ktor.client.statement.bodyAsText
9 | import kotlinx.coroutines.Dispatchers
10 | import kotlinx.coroutines.IO
11 | import kotlinx.coroutines.withContext
12 | import kotlinx.serialization.json.Json
13 |
14 | internal class DefaultCatImagesRepository : CatImagesRepository {
15 | private val client = HttpClient(createKtorEngine())
16 | private val json = Json { ignoreUnknownKeys = true }
17 |
18 | override suspend fun getAll(): List = withContext(Dispatchers.IO) {
19 | val response = client.get(catApiUrl)
20 | val jsonString = response.bodyAsText()
21 | val dto = json.decodeFromString>(jsonString)
22 | return@withContext dto.map { it.toModel() }
23 | }
24 | }
25 |
26 | private fun CatImageDto.toModel(): CatImage {
27 | return CatImage(id, url)
28 | }
29 |
30 | private const val catApiUrl = "https://api.thecatapi.com/v1/images/search?limit=10"
--------------------------------------------------------------------------------
/kmp/cats/shared/src/commonMain/kotlin/com/sample/cats/domain/CatImage.kt:
--------------------------------------------------------------------------------
1 | package com.sample.cats.domain
2 |
3 | data class CatImage(val id: String, val url: String)
--------------------------------------------------------------------------------
/kmp/cats/shared/src/commonMain/kotlin/com/sample/cats/domain/CatImagesRepository.kt:
--------------------------------------------------------------------------------
1 | package com.sample.cats.domain
2 |
3 | internal interface CatImagesRepository {
4 | suspend fun getAll(): List
5 | }
--------------------------------------------------------------------------------
/kmp/cats/shared/src/commonMain/kotlin/com/sample/cats/network/createKtorEngine.kt:
--------------------------------------------------------------------------------
1 | package com.sample.cats.network
2 |
3 | import io.ktor.client.engine.HttpClientEngineFactory
4 |
5 | expect fun createKtorEngine(): HttpClientEngineFactory<*>
--------------------------------------------------------------------------------
/kmp/cats/shared/src/commonMain/kotlin/com/sample/cats/ui/CatImagesScreen.kt:
--------------------------------------------------------------------------------
1 | package com.sample.cats.ui
2 |
3 | import androidx.compose.foundation.clickable
4 | import androidx.compose.foundation.layout.Column
5 | import androidx.compose.foundation.layout.PaddingValues
6 | import androidx.compose.foundation.layout.fillMaxSize
7 | import androidx.compose.foundation.layout.padding
8 | import androidx.compose.foundation.layout.size
9 | import androidx.compose.foundation.lazy.LazyRow
10 | import androidx.compose.foundation.lazy.items
11 | import androidx.compose.material3.Text
12 | import androidx.compose.runtime.Composable
13 | import androidx.compose.runtime.collectAsState
14 | import androidx.compose.runtime.getValue
15 | import androidx.compose.runtime.remember
16 | import androidx.compose.ui.Modifier
17 | import androidx.compose.ui.layout.ContentScale
18 | import androidx.compose.ui.unit.dp
19 | import com.sample.cats.data.DefaultCatImagesRepository
20 | import com.sample.cats.ui.theme.CatImagesViewModel
21 | import io.kamel.image.KamelImage
22 | import io.kamel.image.asyncPainterResource
23 |
24 | @Composable
25 | fun CatImagesScreen(paddingValues: PaddingValues) {
26 | // TODO fix
27 | val viewModel = remember { CatImagesViewModel(repository = DefaultCatImagesRepository()) }
28 | val catImages by viewModel.catImages.collectAsState()
29 | val selectedCatImage by viewModel.selectedCatImage
30 |
31 | Column(Modifier.padding(paddingValues)) {
32 | LazyRow {
33 | items(catImages) { catImage ->
34 | KamelImage(
35 | resource = asyncPainterResource(catImage.url),
36 | contentDescription = null,
37 | modifier = Modifier
38 | .size(100.dp)
39 | .padding(8.dp)
40 | .clickable {
41 | viewModel.selectCat(catImage)
42 | },
43 | contentScale = ContentScale.Crop,
44 | onFailure = { Text("Failed: ${it.message}") },
45 | onLoading = { Text("...") }
46 | )
47 | }
48 | }
49 |
50 | selectedCatImage?.let { selected ->
51 | KamelImage(
52 | resource = asyncPainterResource(selected.url),
53 | contentDescription = null,
54 | modifier = Modifier
55 | .fillMaxSize()
56 | .padding(16.dp),
57 | contentScale = ContentScale.FillWidth
58 | )
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/kmp/cats/shared/src/commonMain/kotlin/com/sample/cats/ui/CatsApp.kt:
--------------------------------------------------------------------------------
1 | package com.sample.cats.ui
2 |
3 | import androidx.compose.material3.ExperimentalMaterial3Api
4 | import androidx.compose.material3.Scaffold
5 | import androidx.compose.material3.Text
6 | import androidx.compose.material3.TopAppBar
7 | import androidx.compose.runtime.Composable
8 | import com.sample.cats.ui.theme.CatsTheme
9 |
10 | @OptIn(ExperimentalMaterial3Api::class)
11 | @Composable
12 | fun CatsApp() {
13 | CatsTheme {
14 | Scaffold(
15 | topBar = { TopAppBar(title = { Text(text = "Cats") }) }
16 | ) {
17 | CatImagesScreen(it)
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/kmp/cats/shared/src/commonMain/kotlin/com/sample/cats/ui/theme/CatImagesViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.sample.cats.ui.theme
2 |
3 | import androidx.compose.runtime.State
4 | import androidx.compose.runtime.mutableStateOf
5 | import com.sample.cats.domain.CatImage
6 | import com.sample.cats.domain.CatImagesRepository
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.GlobalScope
9 | import kotlinx.coroutines.IO
10 | import kotlinx.coroutines.flow.MutableStateFlow
11 | import kotlinx.coroutines.flow.StateFlow
12 | import kotlinx.coroutines.launch
13 | import kotlinx.coroutines.withContext
14 |
15 | internal class CatImagesViewModel constructor(
16 | private val repository: CatImagesRepository
17 | ) {
18 | private val _catImages = MutableStateFlow>(emptyList())
19 | internal val catImages: StateFlow> = _catImages
20 |
21 | private var _selectedCatImage = mutableStateOf(null)
22 | internal val selectedCatImage: State = _selectedCatImage
23 |
24 | init {
25 | // TODO: use VM scope
26 | GlobalScope.launch(Dispatchers.Main) {
27 | withContext(Dispatchers.IO) {
28 | _catImages.value = repository.getAll()
29 | }
30 | }
31 | }
32 |
33 | fun selectCat(cat: CatImage) {
34 | _selectedCatImage.value = cat
35 | }
36 | }
--------------------------------------------------------------------------------
/kmp/cats/shared/src/commonMain/kotlin/com/sample/cats/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.sample.cats.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple80 = Color(0xFFD0BCFF)
6 | val PurpleGrey80 = Color(0xFFCCC2DC)
7 | val Pink80 = Color(0xFFEFB8C8)
8 |
9 | val Purple40 = Color(0xFF6650a4)
10 | val PurpleGrey40 = Color(0xFF625b71)
11 | val Pink40 = Color(0xFF7D5260)
--------------------------------------------------------------------------------
/kmp/cats/shared/src/commonMain/kotlin/com/sample/cats/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.sample.cats.ui.theme
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.material3.MaterialTheme
5 | import androidx.compose.material3.darkColorScheme
6 | import androidx.compose.material3.lightColorScheme
7 | import androidx.compose.runtime.Composable
8 |
9 | private val DarkColorScheme = darkColorScheme(
10 | primary = Purple80,
11 | secondary = PurpleGrey80,
12 | tertiary = Pink80
13 | )
14 |
15 | private val LightColorScheme = lightColorScheme(
16 | primary = Purple40,
17 | secondary = PurpleGrey40,
18 | tertiary = Pink40
19 |
20 | /* Other default colors to override
21 | background = Color(0xFFFFFBFE),
22 | surface = Color(0xFFFFFBFE),
23 | onPrimary = Color.White,
24 | onSecondary = Color.White,
25 | onTertiary = Color.White,
26 | onBackground = Color(0xFF1C1B1F),
27 | onSurface = Color(0xFF1C1B1F),
28 | */
29 | )
30 |
31 | @Composable
32 | internal fun CatsTheme(
33 | darkTheme: Boolean = isSystemInDarkTheme(),
34 | // Dynamic color is available on Android 12+
35 | dynamicColor: Boolean = true,
36 | content: @Composable () -> Unit
37 | ) {
38 | MaterialTheme(
39 | colorScheme = LightColorScheme,
40 | typography = Typography,
41 | content = content
42 | )
43 | }
--------------------------------------------------------------------------------
/kmp/cats/shared/src/commonMain/kotlin/com/sample/cats/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.sample.cats.ui.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp
17 | )
18 | /* Other default text styles to override
19 | titleLarge = TextStyle(
20 | fontFamily = FontFamily.Default,
21 | fontWeight = FontWeight.Normal,
22 | fontSize = 22.sp,
23 | lineHeight = 28.sp,
24 | letterSpacing = 0.sp
25 | ),
26 | labelSmall = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Medium,
29 | fontSize = 11.sp,
30 | lineHeight = 16.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | */
34 | )
--------------------------------------------------------------------------------
/kmp/cats/shared/src/iosMain/kotlin/com/sample/cats/network/createKtorEngine.kt:
--------------------------------------------------------------------------------
1 | package com.sample.cats.network
2 |
3 | import io.ktor.client.engine.HttpClientEngineFactory
4 | import io.ktor.client.engine.darwin.Darwin
5 |
6 | actual fun createKtorEngine(): HttpClientEngineFactory<*> = Darwin
--------------------------------------------------------------------------------
/kmp/cats/shared/src/iosMain/kotlin/main.ios.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.ui.window.ComposeUIViewController
2 | import com.sample.cats.ui.CatsApp
3 |
4 | fun MainViewController() = ComposeUIViewController { CatsApp() }
--------------------------------------------------------------------------------
/native-android/cats/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/native-android/cats/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/native-android/cats/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | id("org.jetbrains.kotlin.plugin.serialization")
5 | }
6 |
7 | android {
8 | namespace = "com.example.cats"
9 | compileSdk = 33
10 |
11 | defaultConfig {
12 | applicationId = "com.example.cats"
13 | minSdk = 21
14 | targetSdk = 33
15 | versionCode = 1
16 | versionName = "1.0"
17 |
18 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
19 | vectorDrawables {
20 | useSupportLibrary = true
21 | }
22 | }
23 |
24 | buildTypes {
25 | release {
26 | isMinifyEnabled = true
27 | isShrinkResources = true
28 | signingConfig = signingConfigs.getByName("debug")
29 | @Suppress("UnstableApiUsage")
30 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
31 | }
32 | create("benchmark") {
33 | initWith(buildTypes.getByName("release"))
34 | matchingFallbacks += listOf("release")
35 | isDebuggable = false
36 | }
37 | }
38 | compileOptions {
39 | sourceCompatibility = JavaVersion.VERSION_17
40 | targetCompatibility = JavaVersion.VERSION_17
41 | }
42 | kotlinOptions {
43 | jvmTarget = "17"
44 | }
45 | @Suppress("UnstableApiUsage")
46 | buildFeatures {
47 | compose = true
48 | }
49 | composeOptions {
50 | kotlinCompilerExtensionVersion = "1.4.3"
51 | }
52 | packaging {
53 | resources {
54 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
55 | }
56 | }
57 | }
58 |
59 | dependencies {
60 | implementation("androidx.core:core-ktx:1.10.1")
61 | implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
62 | implementation("androidx.activity:activity-compose:1.7.2")
63 | implementation(platform("androidx.compose:compose-bom:2023.03.00"))
64 | implementation("androidx.compose.ui:ui")
65 | implementation("androidx.compose.ui:ui-graphics")
66 | implementation("androidx.compose.ui:ui-tooling-preview")
67 | implementation("androidx.compose.material3:material3")
68 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
69 | implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
70 | implementation("io.ktor:ktor-client-android:2.3.3")
71 | implementation("media.kamel:kamel-image:0.7.0")
72 |
73 | testImplementation("junit:junit:4.13.2")
74 | androidTestImplementation("androidx.test.ext:junit:1.1.5")
75 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
76 | androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
77 | androidTestImplementation("androidx.compose.ui:ui-test-junit4")
78 | debugImplementation("androidx.compose.ui:ui-tooling")
79 | debugImplementation("androidx.compose.ui:ui-test-manifest")
80 | }
--------------------------------------------------------------------------------
/native-android/cats/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 | -dontwarn org.slf4j.impl.StaticLoggerBinder
23 | -keep class io.kamel.** { *; }
--------------------------------------------------------------------------------
/native-android/cats/app/src/androidTest/java/com/example/cats/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.cats
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.example.cats", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
17 |
18 |
19 |
20 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/java/com/example/cats/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.cats
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import com.example.cats.ui.CatsApp
7 |
8 | class MainActivity : ComponentActivity() {
9 |
10 | override fun onCreate(savedInstanceState: Bundle?) {
11 | super.onCreate(savedInstanceState)
12 | setContent {
13 | CatsApp()
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/java/com/example/cats/data/CatImageDto.kt:
--------------------------------------------------------------------------------
1 | package com.example.cats.data
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | internal data class CatImageDto(
8 | @SerialName("id")
9 | val id: String,
10 |
11 | @SerialName("url")
12 | val url: String
13 | )
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/java/com/example/cats/data/DefaultCatImagesRepository.kt:
--------------------------------------------------------------------------------
1 | package com.example.cats.data
2 |
3 | import com.example.cats.domain.CatImage
4 | import com.example.cats.domain.CatImagesRepository
5 | import io.ktor.client.HttpClient
6 | import io.ktor.client.engine.android.Android
7 | import io.ktor.client.request.get
8 | import io.ktor.client.statement.bodyAsText
9 | import kotlinx.coroutines.Dispatchers
10 | import kotlinx.coroutines.withContext
11 | import kotlinx.serialization.json.Json
12 |
13 | internal class DefaultCatImagesRepository : CatImagesRepository {
14 | private val client = HttpClient(Android)
15 | private val json = Json { ignoreUnknownKeys = true }
16 |
17 | override suspend fun getAll(): List = withContext(Dispatchers.IO) {
18 | val response = client.get(catApiUrl)
19 | val jsonString = response.bodyAsText()
20 | val dto = json.decodeFromString>(jsonString)
21 | return@withContext dto.map { it.toModel() }
22 | }
23 | }
24 |
25 | private fun CatImageDto.toModel(): CatImage {
26 | return CatImage(id, url)
27 | }
28 |
29 | private const val catApiUrl = "https://api.thecatapi.com/v1/images/search?limit=10"
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/java/com/example/cats/domain/CatImage.kt:
--------------------------------------------------------------------------------
1 | package com.example.cats.domain
2 |
3 | data class CatImage(val id: String, val url: String)
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/java/com/example/cats/domain/CatImagesRepository.kt:
--------------------------------------------------------------------------------
1 | package com.example.cats.domain
2 |
3 | internal interface CatImagesRepository {
4 | suspend fun getAll(): List
5 | }
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/java/com/example/cats/ui/CatImagesScreen.kt:
--------------------------------------------------------------------------------
1 | package com.example.cats.ui
2 |
3 | import androidx.compose.foundation.clickable
4 | import androidx.compose.foundation.layout.Column
5 | import androidx.compose.foundation.layout.PaddingValues
6 | import androidx.compose.foundation.layout.fillMaxSize
7 | import androidx.compose.foundation.layout.padding
8 | import androidx.compose.foundation.layout.size
9 | import androidx.compose.foundation.lazy.LazyRow
10 | import androidx.compose.foundation.lazy.items
11 | import androidx.compose.material3.Text
12 | import androidx.compose.runtime.Composable
13 | import androidx.compose.runtime.collectAsState
14 | import androidx.compose.runtime.getValue
15 | import androidx.compose.runtime.remember
16 | import androidx.compose.ui.Modifier
17 | import androidx.compose.ui.layout.ContentScale
18 | import androidx.compose.ui.unit.dp
19 | import com.example.cats.data.DefaultCatImagesRepository
20 | import com.example.cats.ui.theme.CatImagesViewModel
21 | import io.kamel.image.KamelImage
22 | import io.kamel.image.asyncPainterResource
23 |
24 | @Composable
25 | fun CatImagesScreen(paddingValues: PaddingValues) {
26 | // TODO fix
27 | val viewModel = remember { CatImagesViewModel(repository = DefaultCatImagesRepository()) }
28 | val catImages by viewModel.catImages.collectAsState()
29 | val selectedCatImage by viewModel.selectedCatImage
30 |
31 | Column(Modifier.padding(paddingValues)) {
32 | LazyRow {
33 | items(catImages) { catImage ->
34 | KamelImage(
35 | resource = asyncPainterResource(catImage.url),
36 | contentDescription = null,
37 | modifier = Modifier
38 | .size(100.dp)
39 | .padding(8.dp)
40 | .clickable {
41 | viewModel.selectCat(catImage)
42 | },
43 | contentScale = ContentScale.Crop,
44 | onFailure = { Text("Failed: ${it.message}") },
45 | onLoading = { Text("...") }
46 | )
47 | }
48 | }
49 |
50 | selectedCatImage?.let { selected ->
51 | KamelImage(
52 | resource = asyncPainterResource(selected.url),
53 | contentDescription = null,
54 | modifier = Modifier
55 | .fillMaxSize()
56 | .padding(16.dp),
57 | contentScale = ContentScale.FillWidth
58 | )
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/java/com/example/cats/ui/CatsApp.kt:
--------------------------------------------------------------------------------
1 | package com.example.cats.ui
2 |
3 | import androidx.compose.material3.ExperimentalMaterial3Api
4 | import androidx.compose.material3.Scaffold
5 | import androidx.compose.material3.Text
6 | import androidx.compose.material3.TopAppBar
7 | import androidx.compose.runtime.Composable
8 | import com.example.cats.ui.theme.CatsTheme
9 |
10 | @OptIn(ExperimentalMaterial3Api::class)
11 | @Composable
12 | fun CatsApp() {
13 | CatsTheme {
14 | Scaffold(
15 | topBar = { TopAppBar(title = { Text(text = "Cats") }) }
16 | ) {
17 | CatImagesScreen(it)
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/java/com/example/cats/ui/theme/CatImagesViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.example.cats.ui.theme
2 |
3 | import androidx.compose.runtime.State
4 | import androidx.compose.runtime.mutableStateOf
5 | import com.example.cats.domain.CatImage
6 | import com.example.cats.domain.CatImagesRepository
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.GlobalScope
9 | import kotlinx.coroutines.flow.MutableStateFlow
10 | import kotlinx.coroutines.flow.StateFlow
11 | import kotlinx.coroutines.launch
12 | import kotlinx.coroutines.withContext
13 |
14 | internal class CatImagesViewModel(
15 | private val repository: CatImagesRepository
16 | ) {
17 | private val _catImages = MutableStateFlow>(emptyList())
18 | internal val catImages: StateFlow> = _catImages
19 |
20 | private var _selectedCatImage = mutableStateOf(null)
21 | internal val selectedCatImage: State = _selectedCatImage
22 |
23 | init {
24 | // TODO: use VM scope
25 | GlobalScope.launch(Dispatchers.Main) {
26 | withContext(Dispatchers.IO) {
27 | _catImages.value = repository.getAll()
28 | }
29 | }
30 | }
31 |
32 | fun selectCat(cat: CatImage) {
33 | _selectedCatImage.value = cat
34 | }
35 | }
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/java/com/example/cats/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.example.cats.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple80 = Color(0xFFD0BCFF)
6 | val PurpleGrey80 = Color(0xFFCCC2DC)
7 | val Pink80 = Color(0xFFEFB8C8)
8 |
9 | val Purple40 = Color(0xFF6650a4)
10 | val PurpleGrey40 = Color(0xFF625b71)
11 | val Pink40 = Color(0xFF7D5260)
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/java/com/example/cats/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.example.cats.ui.theme
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.material3.MaterialTheme
5 | import androidx.compose.material3.darkColorScheme
6 | import androidx.compose.material3.lightColorScheme
7 | import androidx.compose.runtime.Composable
8 |
9 | private val DarkColorScheme = darkColorScheme(
10 | primary = Purple80,
11 | secondary = PurpleGrey80,
12 | tertiary = Pink80
13 | )
14 |
15 | private val LightColorScheme = lightColorScheme(
16 | primary = Purple40,
17 | secondary = PurpleGrey40,
18 | tertiary = Pink40
19 |
20 | /* Other default colors to override
21 | background = Color(0xFFFFFBFE),
22 | surface = Color(0xFFFFFBFE),
23 | onPrimary = Color.White,
24 | onSecondary = Color.White,
25 | onTertiary = Color.White,
26 | onBackground = Color(0xFF1C1B1F),
27 | onSurface = Color(0xFF1C1B1F),
28 | */
29 | )
30 |
31 | @Composable
32 | internal fun CatsTheme(
33 | darkTheme: Boolean = isSystemInDarkTheme(),
34 | // Dynamic color is available on Android 12+
35 | dynamicColor: Boolean = true,
36 | content: @Composable () -> Unit
37 | ) {
38 | MaterialTheme(
39 | colorScheme = LightColorScheme,
40 | typography = Typography,
41 | content = content
42 | )
43 | }
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/java/com/example/cats/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.example.cats.ui.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp
17 | )
18 | /* Other default text styles to override
19 | titleLarge = TextStyle(
20 | fontFamily = FontFamily.Default,
21 | fontWeight = FontWeight.Normal,
22 | fontSize = 22.sp,
23 | lineHeight = 28.sp,
24 | letterSpacing = 0.sp
25 | ),
26 | labelSmall = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Medium,
29 | fontSize = 11.sp,
30 | lineHeight = 16.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | */
34 | )
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
10 |
16 |
19 |
22 |
23 |
24 |
25 |
31 |
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
44 |
46 |
48 |
50 |
52 |
54 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
70 |
72 |
74 |
75 |
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/native-android/cats/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/native-android/cats/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/native-android/cats/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/native-android/cats/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/native-android/cats/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/native-android/cats/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/native-android/cats/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/native-android/cats/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/native-android/cats/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/native-android/cats/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Cats
3 |
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
--------------------------------------------------------------------------------
/native-android/cats/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
13 |
14 |
20 |
--------------------------------------------------------------------------------
/native-android/cats/app/src/test/java/com/example/cats/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.cats
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/native-android/cats/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | id("com.android.application") version "8.0.0" apply false
4 | id("org.jetbrains.kotlin.android") version "1.8.10" apply false
5 | id("org.jetbrains.kotlin.plugin.serialization") version "1.8.10" apply false
6 | id("com.android.test") version "8.0.0" apply false
7 | }
--------------------------------------------------------------------------------
/native-android/cats/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/native-android/cats/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/native-android/cats/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/native-android/cats/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Aug 03 16:33:19 CEST 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/native-android/cats/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/native-android/cats/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/native-android/cats/macrobenchmark/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/native-android/cats/macrobenchmark/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.test")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | android {
7 | namespace = "com.example.macrobenchmark"
8 | compileSdk = 33
9 |
10 | compileOptions {
11 | sourceCompatibility = JavaVersion.VERSION_17
12 | targetCompatibility = JavaVersion.VERSION_17
13 | }
14 |
15 | kotlinOptions {
16 | jvmTarget = "17"
17 | }
18 |
19 | defaultConfig {
20 | minSdk = 23
21 | targetSdk = 33
22 |
23 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
24 | }
25 |
26 | buildTypes {
27 | // This benchmark buildType is used for benchmarking, and should function like your
28 | // release build (for example, with minification on). It"s signed with a debug key
29 | // for easy local/CI testing.
30 | create("benchmark") {
31 | isDebuggable = true
32 | signingConfig = getByName("debug").signingConfig
33 | matchingFallbacks += listOf("release")
34 | }
35 | }
36 |
37 | targetProjectPath = ":app"
38 | experimentalProperties["android.experimental.self-instrumenting"] = true
39 | }
40 |
41 | dependencies {
42 | implementation("androidx.test.ext:junit:1.1.5")
43 | implementation("androidx.test.espresso:espresso-core:3.5.1")
44 | implementation("androidx.test.uiautomator:uiautomator:2.2.0")
45 | implementation("androidx.benchmark:benchmark-macro-junit4:1.1.1")
46 | }
47 |
48 | androidComponents {
49 | beforeVariants(selector().all()) {
50 | it.enable = it.buildType == "benchmark"
51 | }
52 | }
--------------------------------------------------------------------------------
/native-android/cats/macrobenchmark/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/native-android/cats/macrobenchmark/src/main/java/com/example/macrobenchmark/ExampleStartupBenchmark.kt:
--------------------------------------------------------------------------------
1 | package com.example.macrobenchmark
2 |
3 | import androidx.benchmark.macro.CompilationMode
4 | import androidx.benchmark.macro.StartupMode
5 | import androidx.benchmark.macro.StartupTimingMetric
6 | import androidx.benchmark.macro.junit4.MacrobenchmarkRule
7 | import androidx.test.ext.junit.runners.AndroidJUnit4
8 | import org.junit.Rule
9 | import org.junit.Test
10 | import org.junit.runner.RunWith
11 |
12 | /**
13 | * This is an example startup benchmark.
14 | *
15 | * It navigates to the device's home screen, and launches the default activity.
16 | *
17 | * Before running this benchmark:
18 | * 1) switch your app's active build variant in the Studio (affects Studio runs only)
19 | * 2) add `` to your app's manifest, within the `` tag
20 | *
21 | * Run this benchmark from Studio to see startup measurements, and captured system traces
22 | * for investigating your app's performance.
23 | */
24 | @RunWith(AndroidJUnit4::class)
25 | class ExampleStartupBenchmark {
26 | @get:Rule
27 | val benchmarkRule = MacrobenchmarkRule()
28 |
29 | @Test
30 | fun startup() = benchmarkRule.measureRepeated(
31 | packageName = "com.example.cats",
32 | metrics = listOf(StartupTimingMetric()),
33 | iterations = 5,
34 | startupMode = StartupMode.COLD
35 | ) {
36 | pressHome()
37 | startActivityAndWait()
38 | }
39 | }
--------------------------------------------------------------------------------
/native-android/cats/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 |
16 | rootProject.name = "Cats"
17 | include(":app")
18 | include(":macrobenchmark")
19 |
--------------------------------------------------------------------------------
/native-ios/Cats/Cats.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1623BE052A7CF8CF0089EC7C /* CatsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1623BE042A7CF8CF0089EC7C /* CatsApp.swift */; };
11 | 1623BE072A7CF8CF0089EC7C /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1623BE062A7CF8CF0089EC7C /* ContentView.swift */; };
12 | 1623BE092A7CF8D00089EC7C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1623BE082A7CF8D00089EC7C /* Assets.xcassets */; };
13 | 1623BE0C2A7CF8D00089EC7C /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1623BE0B2A7CF8D00089EC7C /* Preview Assets.xcassets */; };
14 | 1623BE142A7CF97C0089EC7C /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = 1623BE132A7CF97C0089EC7C /* AlamofireImage */; };
15 | 1623BE162A7CF9AD0089EC7C /* CatImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1623BE152A7CF9AD0089EC7C /* CatImage.swift */; };
16 | 1623BE182A7CF9D10089EC7C /* CatImagesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1623BE172A7CF9D10089EC7C /* CatImagesViewModel.swift */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXFileReference section */
20 | 1623BE012A7CF8CF0089EC7C /* Cats.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Cats.app; sourceTree = BUILT_PRODUCTS_DIR; };
21 | 1623BE042A7CF8CF0089EC7C /* CatsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatsApp.swift; sourceTree = ""; };
22 | 1623BE062A7CF8CF0089EC7C /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
23 | 1623BE082A7CF8D00089EC7C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
24 | 1623BE0B2A7CF8D00089EC7C /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
25 | 1623BE152A7CF9AD0089EC7C /* CatImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatImage.swift; sourceTree = ""; };
26 | 1623BE172A7CF9D10089EC7C /* CatImagesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatImagesViewModel.swift; sourceTree = ""; };
27 | /* End PBXFileReference section */
28 |
29 | /* Begin PBXFrameworksBuildPhase section */
30 | 1623BDFE2A7CF8CF0089EC7C /* Frameworks */ = {
31 | isa = PBXFrameworksBuildPhase;
32 | buildActionMask = 2147483647;
33 | files = (
34 | 1623BE142A7CF97C0089EC7C /* AlamofireImage in Frameworks */,
35 | );
36 | runOnlyForDeploymentPostprocessing = 0;
37 | };
38 | /* End PBXFrameworksBuildPhase section */
39 |
40 | /* Begin PBXGroup section */
41 | 1623BDF82A7CF8CE0089EC7C = {
42 | isa = PBXGroup;
43 | children = (
44 | 1623BE032A7CF8CF0089EC7C /* Cats */,
45 | 1623BE022A7CF8CF0089EC7C /* Products */,
46 | );
47 | sourceTree = "";
48 | };
49 | 1623BE022A7CF8CF0089EC7C /* Products */ = {
50 | isa = PBXGroup;
51 | children = (
52 | 1623BE012A7CF8CF0089EC7C /* Cats.app */,
53 | );
54 | name = Products;
55 | sourceTree = "";
56 | };
57 | 1623BE032A7CF8CF0089EC7C /* Cats */ = {
58 | isa = PBXGroup;
59 | children = (
60 | 1623BE042A7CF8CF0089EC7C /* CatsApp.swift */,
61 | 1623BE062A7CF8CF0089EC7C /* ContentView.swift */,
62 | 1623BE082A7CF8D00089EC7C /* Assets.xcassets */,
63 | 1623BE0A2A7CF8D00089EC7C /* Preview Content */,
64 | 1623BE152A7CF9AD0089EC7C /* CatImage.swift */,
65 | 1623BE172A7CF9D10089EC7C /* CatImagesViewModel.swift */,
66 | );
67 | path = Cats;
68 | sourceTree = "";
69 | };
70 | 1623BE0A2A7CF8D00089EC7C /* Preview Content */ = {
71 | isa = PBXGroup;
72 | children = (
73 | 1623BE0B2A7CF8D00089EC7C /* Preview Assets.xcassets */,
74 | );
75 | path = "Preview Content";
76 | sourceTree = "";
77 | };
78 | /* End PBXGroup section */
79 |
80 | /* Begin PBXNativeTarget section */
81 | 1623BE002A7CF8CF0089EC7C /* Cats */ = {
82 | isa = PBXNativeTarget;
83 | buildConfigurationList = 1623BE0F2A7CF8D00089EC7C /* Build configuration list for PBXNativeTarget "Cats" */;
84 | buildPhases = (
85 | 1623BDFD2A7CF8CF0089EC7C /* Sources */,
86 | 1623BDFE2A7CF8CF0089EC7C /* Frameworks */,
87 | 1623BDFF2A7CF8CF0089EC7C /* Resources */,
88 | );
89 | buildRules = (
90 | );
91 | dependencies = (
92 | );
93 | name = Cats;
94 | packageProductDependencies = (
95 | 1623BE132A7CF97C0089EC7C /* AlamofireImage */,
96 | );
97 | productName = Cats;
98 | productReference = 1623BE012A7CF8CF0089EC7C /* Cats.app */;
99 | productType = "com.apple.product-type.application";
100 | };
101 | /* End PBXNativeTarget section */
102 |
103 | /* Begin PBXProject section */
104 | 1623BDF92A7CF8CE0089EC7C /* Project object */ = {
105 | isa = PBXProject;
106 | attributes = {
107 | BuildIndependentTargetsInParallel = 1;
108 | LastSwiftUpdateCheck = 1430;
109 | LastUpgradeCheck = 1430;
110 | TargetAttributes = {
111 | 1623BE002A7CF8CF0089EC7C = {
112 | CreatedOnToolsVersion = 14.3.1;
113 | };
114 | };
115 | };
116 | buildConfigurationList = 1623BDFC2A7CF8CE0089EC7C /* Build configuration list for PBXProject "Cats" */;
117 | compatibilityVersion = "Xcode 14.0";
118 | developmentRegion = en;
119 | hasScannedForEncodings = 0;
120 | knownRegions = (
121 | en,
122 | Base,
123 | );
124 | mainGroup = 1623BDF82A7CF8CE0089EC7C;
125 | packageReferences = (
126 | 1623BE122A7CF97C0089EC7C /* XCRemoteSwiftPackageReference "AlamofireImage" */,
127 | );
128 | productRefGroup = 1623BE022A7CF8CF0089EC7C /* Products */;
129 | projectDirPath = "";
130 | projectRoot = "";
131 | targets = (
132 | 1623BE002A7CF8CF0089EC7C /* Cats */,
133 | );
134 | };
135 | /* End PBXProject section */
136 |
137 | /* Begin PBXResourcesBuildPhase section */
138 | 1623BDFF2A7CF8CF0089EC7C /* Resources */ = {
139 | isa = PBXResourcesBuildPhase;
140 | buildActionMask = 2147483647;
141 | files = (
142 | 1623BE0C2A7CF8D00089EC7C /* Preview Assets.xcassets in Resources */,
143 | 1623BE092A7CF8D00089EC7C /* Assets.xcassets in Resources */,
144 | );
145 | runOnlyForDeploymentPostprocessing = 0;
146 | };
147 | /* End PBXResourcesBuildPhase section */
148 |
149 | /* Begin PBXSourcesBuildPhase section */
150 | 1623BDFD2A7CF8CF0089EC7C /* Sources */ = {
151 | isa = PBXSourcesBuildPhase;
152 | buildActionMask = 2147483647;
153 | files = (
154 | 1623BE072A7CF8CF0089EC7C /* ContentView.swift in Sources */,
155 | 1623BE182A7CF9D10089EC7C /* CatImagesViewModel.swift in Sources */,
156 | 1623BE162A7CF9AD0089EC7C /* CatImage.swift in Sources */,
157 | 1623BE052A7CF8CF0089EC7C /* CatsApp.swift in Sources */,
158 | );
159 | runOnlyForDeploymentPostprocessing = 0;
160 | };
161 | /* End PBXSourcesBuildPhase section */
162 |
163 | /* Begin XCBuildConfiguration section */
164 | 1623BE0D2A7CF8D00089EC7C /* Debug */ = {
165 | isa = XCBuildConfiguration;
166 | buildSettings = {
167 | ALWAYS_SEARCH_USER_PATHS = NO;
168 | CLANG_ANALYZER_NONNULL = YES;
169 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
170 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
171 | CLANG_ENABLE_MODULES = YES;
172 | CLANG_ENABLE_OBJC_ARC = YES;
173 | CLANG_ENABLE_OBJC_WEAK = YES;
174 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
175 | CLANG_WARN_BOOL_CONVERSION = YES;
176 | CLANG_WARN_COMMA = YES;
177 | CLANG_WARN_CONSTANT_CONVERSION = YES;
178 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
179 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
180 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
181 | CLANG_WARN_EMPTY_BODY = YES;
182 | CLANG_WARN_ENUM_CONVERSION = YES;
183 | CLANG_WARN_INFINITE_RECURSION = YES;
184 | CLANG_WARN_INT_CONVERSION = YES;
185 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
186 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
187 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
188 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
189 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
190 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
191 | CLANG_WARN_STRICT_PROTOTYPES = YES;
192 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
193 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
194 | CLANG_WARN_UNREACHABLE_CODE = YES;
195 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
196 | COPY_PHASE_STRIP = NO;
197 | DEBUG_INFORMATION_FORMAT = dwarf;
198 | ENABLE_STRICT_OBJC_MSGSEND = YES;
199 | ENABLE_TESTABILITY = YES;
200 | GCC_C_LANGUAGE_STANDARD = gnu11;
201 | GCC_DYNAMIC_NO_PIC = NO;
202 | GCC_NO_COMMON_BLOCKS = YES;
203 | GCC_OPTIMIZATION_LEVEL = 0;
204 | GCC_PREPROCESSOR_DEFINITIONS = (
205 | "DEBUG=1",
206 | "$(inherited)",
207 | );
208 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
209 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
210 | GCC_WARN_UNDECLARED_SELECTOR = YES;
211 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
212 | GCC_WARN_UNUSED_FUNCTION = YES;
213 | GCC_WARN_UNUSED_VARIABLE = YES;
214 | IPHONEOS_DEPLOYMENT_TARGET = 16.4;
215 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
216 | MTL_FAST_MATH = YES;
217 | ONLY_ACTIVE_ARCH = YES;
218 | SDKROOT = iphoneos;
219 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
220 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
221 | };
222 | name = Debug;
223 | };
224 | 1623BE0E2A7CF8D00089EC7C /* Release */ = {
225 | isa = XCBuildConfiguration;
226 | buildSettings = {
227 | ALWAYS_SEARCH_USER_PATHS = NO;
228 | CLANG_ANALYZER_NONNULL = YES;
229 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
230 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
231 | CLANG_ENABLE_MODULES = YES;
232 | CLANG_ENABLE_OBJC_ARC = YES;
233 | CLANG_ENABLE_OBJC_WEAK = YES;
234 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
235 | CLANG_WARN_BOOL_CONVERSION = YES;
236 | CLANG_WARN_COMMA = YES;
237 | CLANG_WARN_CONSTANT_CONVERSION = YES;
238 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
239 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
240 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
241 | CLANG_WARN_EMPTY_BODY = YES;
242 | CLANG_WARN_ENUM_CONVERSION = YES;
243 | CLANG_WARN_INFINITE_RECURSION = YES;
244 | CLANG_WARN_INT_CONVERSION = YES;
245 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
246 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
247 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
248 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
249 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
250 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
251 | CLANG_WARN_STRICT_PROTOTYPES = YES;
252 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
253 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
254 | CLANG_WARN_UNREACHABLE_CODE = YES;
255 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
256 | COPY_PHASE_STRIP = NO;
257 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
258 | ENABLE_NS_ASSERTIONS = NO;
259 | ENABLE_STRICT_OBJC_MSGSEND = YES;
260 | GCC_C_LANGUAGE_STANDARD = gnu11;
261 | GCC_NO_COMMON_BLOCKS = YES;
262 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
263 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
264 | GCC_WARN_UNDECLARED_SELECTOR = YES;
265 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
266 | GCC_WARN_UNUSED_FUNCTION = YES;
267 | GCC_WARN_UNUSED_VARIABLE = YES;
268 | IPHONEOS_DEPLOYMENT_TARGET = 16.4;
269 | MTL_ENABLE_DEBUG_INFO = NO;
270 | MTL_FAST_MATH = YES;
271 | SDKROOT = iphoneos;
272 | SWIFT_COMPILATION_MODE = wholemodule;
273 | SWIFT_OPTIMIZATION_LEVEL = "-O";
274 | VALIDATE_PRODUCT = YES;
275 | };
276 | name = Release;
277 | };
278 | 1623BE102A7CF8D00089EC7C /* Debug */ = {
279 | isa = XCBuildConfiguration;
280 | buildSettings = {
281 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
282 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
283 | CODE_SIGN_STYLE = Automatic;
284 | CURRENT_PROJECT_VERSION = 1;
285 | DEVELOPMENT_ASSET_PATHS = "\"Cats/Preview Content\"";
286 | ENABLE_PREVIEWS = YES;
287 | GENERATE_INFOPLIST_FILE = YES;
288 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
289 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
290 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
291 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
292 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
293 | LD_RUNPATH_SEARCH_PATHS = (
294 | "$(inherited)",
295 | "@executable_path/Frameworks",
296 | );
297 | MARKETING_VERSION = 1.0;
298 | PRODUCT_BUNDLE_IDENTIFIER = com.example.Cats;
299 | PRODUCT_NAME = "$(TARGET_NAME)";
300 | SWIFT_EMIT_LOC_STRINGS = YES;
301 | SWIFT_VERSION = 5.0;
302 | TARGETED_DEVICE_FAMILY = "1,2";
303 | };
304 | name = Debug;
305 | };
306 | 1623BE112A7CF8D00089EC7C /* Release */ = {
307 | isa = XCBuildConfiguration;
308 | buildSettings = {
309 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
310 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
311 | CODE_SIGN_STYLE = Automatic;
312 | CURRENT_PROJECT_VERSION = 1;
313 | DEVELOPMENT_ASSET_PATHS = "\"Cats/Preview Content\"";
314 | ENABLE_PREVIEWS = YES;
315 | GENERATE_INFOPLIST_FILE = YES;
316 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
317 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
318 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
319 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
320 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
321 | LD_RUNPATH_SEARCH_PATHS = (
322 | "$(inherited)",
323 | "@executable_path/Frameworks",
324 | );
325 | MARKETING_VERSION = 1.0;
326 | PRODUCT_BUNDLE_IDENTIFIER = com.example.Cats;
327 | PRODUCT_NAME = "$(TARGET_NAME)";
328 | SWIFT_EMIT_LOC_STRINGS = YES;
329 | SWIFT_VERSION = 5.0;
330 | TARGETED_DEVICE_FAMILY = "1,2";
331 | };
332 | name = Release;
333 | };
334 | /* End XCBuildConfiguration section */
335 |
336 | /* Begin XCConfigurationList section */
337 | 1623BDFC2A7CF8CE0089EC7C /* Build configuration list for PBXProject "Cats" */ = {
338 | isa = XCConfigurationList;
339 | buildConfigurations = (
340 | 1623BE0D2A7CF8D00089EC7C /* Debug */,
341 | 1623BE0E2A7CF8D00089EC7C /* Release */,
342 | );
343 | defaultConfigurationIsVisible = 0;
344 | defaultConfigurationName = Release;
345 | };
346 | 1623BE0F2A7CF8D00089EC7C /* Build configuration list for PBXNativeTarget "Cats" */ = {
347 | isa = XCConfigurationList;
348 | buildConfigurations = (
349 | 1623BE102A7CF8D00089EC7C /* Debug */,
350 | 1623BE112A7CF8D00089EC7C /* Release */,
351 | );
352 | defaultConfigurationIsVisible = 0;
353 | defaultConfigurationName = Release;
354 | };
355 | /* End XCConfigurationList section */
356 |
357 | /* Begin XCRemoteSwiftPackageReference section */
358 | 1623BE122A7CF97C0089EC7C /* XCRemoteSwiftPackageReference "AlamofireImage" */ = {
359 | isa = XCRemoteSwiftPackageReference;
360 | repositoryURL = "https://github.com/Alamofire/AlamofireImage";
361 | requirement = {
362 | kind = upToNextMajorVersion;
363 | minimumVersion = 4.0.0;
364 | };
365 | };
366 | /* End XCRemoteSwiftPackageReference section */
367 |
368 | /* Begin XCSwiftPackageProductDependency section */
369 | 1623BE132A7CF97C0089EC7C /* AlamofireImage */ = {
370 | isa = XCSwiftPackageProductDependency;
371 | package = 1623BE122A7CF97C0089EC7C /* XCRemoteSwiftPackageReference "AlamofireImage" */;
372 | productName = AlamofireImage;
373 | };
374 | /* End XCSwiftPackageProductDependency section */
375 | };
376 | rootObject = 1623BDF92A7CF8CE0089EC7C /* Project object */;
377 | }
378 |
--------------------------------------------------------------------------------
/native-ios/Cats/Cats.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/native-ios/Cats/Cats.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/native-ios/Cats/Cats.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "alamofire",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/Alamofire/Alamofire.git",
7 | "state" : {
8 | "revision" : "bc268c28fb170f494de9e9927c371b8342979ece",
9 | "version" : "5.7.1"
10 | }
11 | },
12 | {
13 | "identity" : "alamofireimage",
14 | "kind" : "remoteSourceControl",
15 | "location" : "https://github.com/Alamofire/AlamofireImage",
16 | "state" : {
17 | "revision" : "98cbb00ce0ec5fc8e52a5b50a6bfc08d3e5aee10",
18 | "version" : "4.2.0"
19 | }
20 | }
21 | ],
22 | "version" : 2
23 | }
24 |
--------------------------------------------------------------------------------
/native-ios/Cats/Cats.xcodeproj/project.xcworkspace/xcuserdata/jacob.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobras/flutter-vs-native-vs-kmp/10b4bc8ff32f2550f3d87a7585e39e3dc89e2843/native-ios/Cats/Cats.xcodeproj/project.xcworkspace/xcuserdata/jacob.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/native-ios/Cats/Cats.xcodeproj/xcuserdata/jacob.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Cats.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/native-ios/Cats/Cats/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/native-ios/Cats/Cats/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/native-ios/Cats/Cats/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/native-ios/Cats/Cats/CatImage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CatImage.swift
3 | // Cats
4 | //
5 | // Created by Jacob on 04.08.2023.
6 | //
7 |
8 | import Foundation
9 |
10 | struct CatImage : Identifiable {
11 | let id: String
12 | let url: URL
13 | }
14 |
--------------------------------------------------------------------------------
/native-ios/Cats/Cats/CatImagesViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CatImagesViewModel.swift
3 | // Cats
4 | //
5 | // Created by Jacob on 04.08.2023.
6 | //
7 |
8 | import Foundation
9 | import Alamofire
10 |
11 | class CatImagesViewModel: ObservableObject {
12 | @Published var catImages: [CatImage] = []
13 |
14 | init() {
15 | fetchCatImages()
16 | }
17 |
18 | func fetchCatImages() {
19 | let apiUrl = "https://api.thecatapi.com/v1/images/search?limit=10"
20 | AF.request(apiUrl).responseJSON { response in
21 | if let data = response.data {
22 | do {
23 | let json = try JSONSerialization.jsonObject(with: data, options: [])
24 | if let jsonArray = json as? [[String: Any]] {
25 | let images = jsonArray.compactMap { json -> CatImage? in
26 | guard let id = json["id"] as? String,
27 | let urlString = json["url"] as? String,
28 | let url = URL(string: urlString) else {
29 | return nil
30 | }
31 | return CatImage(id: id, url: url)
32 | }
33 | DispatchQueue.main.async {
34 | self.catImages = images
35 | }
36 | }
37 | } catch {
38 | print("Failed to parse JSON: \(error)")
39 | }
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/native-ios/Cats/Cats/CatsApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CatsApp.swift
3 | // Cats
4 | //
5 | // Created by Jacob on 04.08.2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct CatsApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/native-ios/Cats/Cats/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Cats
4 | //
5 | // Created by Jacob on 04.08.2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ContentView: View {
11 | @StateObject private var viewModel = CatImagesViewModel()
12 | @State private var selectedImage: CatImage?
13 |
14 | var body: some View {
15 | VStack(alignment: .leading) {
16 | ScrollView(.horizontal, showsIndicators: false) {
17 | HStack {
18 | ForEach(viewModel.catImages) { catImage in
19 | Image(uiImage: UIImage(url: catImage.url)!)
20 | .resizable()
21 | .scaledToFit()
22 | .frame(width: 100, height: 100)
23 | .padding(8)
24 | .onTapGesture {
25 | selectedImage = catImage
26 | }
27 | }
28 | }
29 | }.frame(maxHeight: 100)
30 |
31 | Spacer()
32 |
33 | if let selectedImage = selectedImage {
34 | Image(uiImage: UIImage(url: selectedImage.url)!)
35 | .resizable()
36 | .scaledToFit()
37 | .padding(16)
38 | }
39 | }
40 | .padding(.top, 16)
41 | }
42 | }
43 |
44 | struct ContentView_Previews: PreviewProvider {
45 | static var previews: some View {
46 | ContentView()
47 | }
48 | }
49 |
50 | extension UIImage {
51 | convenience init?(url: URL) {
52 | do {
53 | let data = try Data(contentsOf: url)
54 | self.init(data: data)
55 | } catch {
56 | print("Failed to load image: \(error)")
57 | return nil
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/native-ios/Cats/Cats/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------