Majid Hajian
mhadaily
mhadaily
mhadaily
mhadaily
import 'package:flutter/material.dart';
MaterialApp(
ThemeData(
name: "Majid Hajian",
location: "Oslo, Norway",
description: '''
Google Developer Expert
Passionate Software engineer,
Community Leader, Author and international Speaker
''',
main: "Flutter/Dart, PWA, Performance",
homepage: "https://www.majidhajian.com",
socials: {
twitter: "https://www.twitter.com/mhadaily",
github: "https://www.github.com/mhadaily"
},
author: {
Pluralsight: "www.pluralsight.com/authors/majid-hajian",
Apress: "Progressive Web App with Angular, Book",
PacktPub: "PWA development",
Udemy: "PWA development",
}
founder: "Softiware As (www.Softiware.com)"
devDependencies: {
tea: "Ginger",
mac: "10.14+",
},
community: {
MobileEraConference: "Orginizer",
FlutterVikings: "Orginizer",
FlutterDartOslo: "Orginizer",
GDGOslo: "Co-Orginizer",
DevFestNorway: "Orginizer",
...more
}));
Find me on the internet by
The Flow
mhadaily
Company Testers
Internal Testers
External Testers / Beta
Production
mhadaily
Scrum
mhadaily
mhadaily
Company Testers
Internal Testers
External Testers / Beta
Production
mhadaily
if [[ "$FH_BRANCH" == "master" ]]; then
export DEPLOYMENT_LANE="production"
elif [[ "$FH_BRANCH" == "develop" ]]; then
export DEPLOYMENT_LANE="internal"
elif [[ "$FH_BRANCH" =~ ^release\/.*$ ]]; then
export DEPLOYMENT_LANE="beta"
else
export DEPLOYMENT_LANE="feature"
fi
Production
Beta
Production
Alpha
Internal
App Store
TestFlight
End Users
Internal Testers
Beta Testers
mhadaily
mhadaily
mhadaily
void main(List<String> arguments) {
analyze.main();
format.main();
test.main();
}
mhadaily
void main(List<String> arguments) {
analyze.main();
format.main();
test.main();
}
void main() {
stdout.write('Start Analyzing...\n');
final ProcessResult result = Process.runSync(
'flutter',
<String>['analyze', '--no-pub'],
runInShell: true,
);
checkResult(result);
}
mhadaily
mhadaily
#!/bin/bash
# Created For Majid's App
if [ ! -f /Users/majid/Projects/app/scripts/git_hooks/pre_commit.dart ]; then
# Dart script not found, skipping hook"
exit 0
fi
echo "> Running pre_commit hook..."
DART_EXIT_CODE=0
dart /Users/majid/Projects/app/scripts/git_hooks/pre_commit.dart "$@"
DART_EXIT_CODE=$?
if [[ ${DART_EXIT_CODE} -ne 0 ]]; then
echo ""
echo "> Error detected in pre_commit hook."
exit 1
fi
> cat .git/hooks/pre-commit
mhadaily
mhadaily
mhadaily
mhadaily
mhadaily
mhadaily
fastlane is an open source platform aimed at simplifying Android and iOS deployment.
fastlane lets you automate every aspect of your development and release workflow.
mhadaily
mhadaily
.
├── Appfile
├── Deliverfile
├── Fastfile
├── Matchfile
├── Pluginfile
├── android
│ ├── icons
│ ├── metadata
│ └── screenshots
├── devices.txt
├── ios
│ ├── metadata
│ ├── push_certificates
│ └── screenshots
└── report.xml
.
├── CHANGELOG.md
├── Gemfile
├── Gemfile.lock
├── README.md
├── analysis_options.yaml
├── android
├── assets
├── build
├── build.yaml
├── dartdoc_options.yaml
├── docs
├── fastlane
├── gen
├── github_action.sh
├── ios
├── lib
├── local_packages
├── pubspec.lock
├── pubspec.yaml
├── res
├── scripts
├── setup.dart
├── test
├── travis_before_script.sh
├── travis_script.sh
└── web
platform :android do
# your lanes for Android here
end
platform :ios do
# your lanes for iOS here
end
mhadaily
Fastfile
platform :android do
desc "Build and release to beta"
lane :beta_internal_release do
prepare(release: false)
setup_new_changelog
release_to_store(track: 'internal')
end
end
mhadaily
platform :ios do
desc "Push a new release to TestFlight"
lane :build_and_deploy_testflight do |options|
puts "Build and Deploy TestFlight started..."
prepare(environment: options[:environment])
setup_new_changelog
upload_to_testflight(
skip_waiting_for_build_processing: true,
ipa: "Runner.ipa",
username: username,
#changelog: changelog, # it's possible to
#add AppInfo, BuildInfo and more, also localize it.
)
slack(
message: "#{version_number}+#{build_number}: iOS beta released to
TestFlight #{options[:channel]} \n Changelog: \n #{emojified_changelog}",
slack_url: slack_url,
)
# git_actions(release: false)
end
end
mhadaily
platform :ios do
desc "Prepare and archive app"
lane :prepare do |options|
puts "Building and deploying version #{version_number}, current build #{latest_testflight_build_number}"
if (ENV["AWS_ACCESS_KEY_ID"])
push_notification_certificates
end
handle_versioning
flutter_build_no_sign
generate_icons
beta_badge(hide: options[:release])
code_signing
build_ios_app(
workspace: workspace_path,
configuration: "Release",
scheme: "Runner",
export_team_id: export_team_id,
export_method: "app-store",
# Verify that the right signing identity is used for publishing.
codesigning_identity: codesigning_identity,
xcargs: "DEVELOPMENT_TEAM='#{export_team_id}'"
)
end
end
mhadaily
Share one code signing identity across your development team to simplify your codesigning setup and prevent code signing issues.
mhadaily
platform :ios do
desc "Sing code using Match and fastlane"
lane :code_signing do
# Stop fastlane from echoing back ENV var.
# Doesn't matter too much since Travis doesn't echo back encrypted variables
# anyway :D
suppress_output {
# Retrieves all the necessary certs and provisioning profiles.
# match # alias for "sync_code_signing"
sync_code_signing(readonly: true)
}
puts 'Certificates and profiles installed'
# Modify the Xcode project to use the new team and profile.
# It will put the git state to dirty but Travis will be wiped after
# then run session.
disable_automatic_code_signing
update_project_provisioning(
build_configuration: 'Release',
profile: ENV["sigh_#{app_identifier}_appstore_profile-path"]
)
end
end
mhadaily
#Matchfile
git_url(ENV['REPO'])
storage_mode("git")
type("appstore") # The default type, can be: appstore, adhoc, enterprise or development
app_identifier(["com.your.app"])
username(ENV['USERNAME']) # Your Apple Developer Portal username
or
mhadaily
platform :ios do
desc "Handle version and build number"
lane :handle_versioning do
build_number = latest_testflight_build_number + 1
puts "new build number now is
#{build_number} and build
version is #{version_number}"
end
end
platform :android do
desc "Handle version and build number"
lane :handle_versioning do
internal_number = google_play_track_version_codes(track: 'internal')[0].to_i
beta_number = google_play_track_version_codes(track: 'beta')[0].to_i
alpha_number = google_play_track_version_codes(track: 'alpha')[0].to_i
production_number = google_play_track_version_codes(track: 'production')[0].to_i
# make sure we know all release number and bump latest version
build_number = [internal_number, beta_number, production_number, alpha_number].max + 1
puts "new build number now is #{build_number} and build version is #{raw_version}"
end
end
mhadaily
platform :ios do
desc "Build app with no signing for ios and increase build number or version number"
lane :flutter_build_no_sign do
Dir.chdir "../#{project_ios_path}" do
sh("flutter", "doctor")
sh("flutter", "packages", "get")
sh("flutter", "clean")
sh("flutter", "test")
end
end
end
Run Test / other commands
mhadaily
platform :android do
desc "Create change log and make it ready for both slack and google play"
lane :setup_new_changelog do
changelog = read_changelog(
changelog_path: "../CHANGELOG.md",
section_identifier: "[Build: #{version_number}]", # Specify what section to read
excluded_markdown_elements: ['-', '###'] # Specify which markdown elements should be excluded
)
emojified_changelog = emojify_changelog
# write for Google store at the moment we have only no-NO
path = "./metadata/android/no-NO/changelogs"
dir = File.dirname(path)
unless File.directory?(dir)
FileUtils.mkdir_p(dir)
end
path << "/#{build_number}.txt"
File.new(path, 'w')
File.write(path, changelog)
puts "Changelog for version #{version_number}+#{build_number}: #{changelog}"
end
end
Change Log / Metadata
mhadaily
platform :android do
desc "update git"
lane :git_actions do
# Ensure that your git status is not dirty
ensure_git_status_clean
#add git tag
add_git_tag(build_number: version_number)
# Commit the version bump
commit_version_bump
# Push the new commit and tag back to your git remote
push_to_git_remote
end
# and many more
# capture screenshots for android
# upload to Google Play
# Run tests
# generate icons
# add beta badge
# lots of other plugins
end
more Lanes
mhadaily
desc "Build and release to internal tester"
lane :internal do
prepare(release: false, environment: 'development')
setup_new_changelog
release_to_store(track: 'internal')
end
desc "Deploy a new version to the Google Play production / First to beta and then we can promote to Production Manually"
lane :prod do
prepare(release: true, environment: 'production')
setup_new_changelog
release_to_store(track: "beta")
end
desc "Deploy a new version to the Google Play Beta"
lane :beta do
prepare(release: true, environment: 'production')
setup_new_changelog
release_to_store(track: "alpha")
end
Deployment Lanes
mhadaily
if [[ "$1" == "ios" ]] || [[ "$1" == "android" ]]; then
bundle exec fastlane "$1" "$DEPLOYMENT_LANE"
fi
mhadaily
Resource Capacity
Project Complexity
Resource Capacity
Project Complexity
mhadaily
customer
id
name
interest
customer
id
name
phone
Support
customer
id
address
availability
customer
id
name
payment method
Accounting
By
Namespace
Folder
Project
mhadaily
customer
id
name
interest
Sales
customer
id
name
phone
Employees
mhadaily
customer
id
name
interest
Sales
customer
id
name
phone
Employees
customer
id
address
availability
Marketing
customer
id
name
payment method
Accounting
mhadaily
customer
id
name
interest
Sales
customer
id
name
phone
Employees
customer
id
address
availability
Marketing
customer
id
name
payment method
Accounting
Shared
Kernel
e.g.
USER
Authentication
mhadaily
customer
id
name
interest
Sales
customer
id
name
phone
Support
customer
id
address
availability
Order
customer
id
name
payment method
Accounting
Open Host Service
mhadaily
Sales
Catalog
E-commerce App
Domain
Domain
Infrastructure
Infrastructure
Presentation
Presentation
Application
Application
mhadaily
https://slides.com/mhadaily/flutter-architecture-inspired-strategic-domain-driven-design
mhadaily
├── lib
│ ├── catalog
│ │ ├── api
│ │ │ └── index.dart
│ │ ├── application
│ │ │ └── catalog_facade_service.dart
│ │ ├── domain
│ │ │ ├── entities
│ │ │ │ └── product.dart
│ │ │ ├── interfaces
│ │ │ │ └── product_interface.dart
│ │ │ └── logic
│ │ │ ├── catalog_logic.dart
│ │ │ └── get_official_product_manufacturer.dart
│ │ ├── infrastructure
│ │ │ ├── data_sources
│ │ │ │ ├── product_local_data_provider.dart
│ │ │ │ └── product_remote_data_provider.dart
│ │ │ ├── models
│ │ │ │ └── product_model.dart
│ │ │ └── repositories
│ │ │ └── product_repository.dart
│ │ └── presentation
│ │ ├── catalog-detail
│ │ │ ├── bloc
│ │ │ │ ├── bloc.dart
│ │ │ │ ├── catalog_detail_bloc.dart
│ │ │ │ ├── catalog_detail_event.dart
│ │ │ │ └── catalog_detail_state.dart
│ │ │ └── catalog_detail_screen.dart
│ │ ├── catalog-list
│ │ │ ├── bloc
│ │ │ │ ├── bloc.dart
│ │ │ │ ├── catalog_list_bloc.dart
│ │ │ │ ├── catalog_list_event.dart
│ │ │ │ └── catalog_list_state.dart
│ │ │ └── catalog_list_screen.dart
│ │ └── widgets
│ │ └── catalog_name.dart
│ ├── common
│ │ ├── config
│ │ │ └── config.dart
│ │ ├── exceptions
│ │ │ ├── exceptions.dart
│ │ │ ├── local_exceptions.dart
│ │ │ └── server_exceptions.dart
│ │ ├── helpers
│ │ │ └── helpers.dart
│ │ ├── platform
│ │ │ └── connectivity.dart
│ │ ├── utils
│ │ │ └── utils.dart
│ │ └── widgets
│ │ └── widgets.dart
│ ├── injections.dart
│ └── main.dart
mhadaily
Advanced
── catalog
│ ├── logic
│ │ ├── catalog_logic.dart
│ │ ├── get_official_product_manufacturer.dart
│ │ └── product_repository.dart
│ └── presentation
│ ├── catalog-detail
│ │ ├── bloc
│ │ │ ├── bloc.dart
│ │ │ ├── catalog_detail_bloc.dart
│ │ │ ├── catalog_detail_event.dart
│ │ │ └── catalog_detail_state.dart
│ │ └── catalog_detail_screen.dart
│ ├── catalog-list
│ │ ├── bloc
│ │ │ ├── bloc.dart
│ │ │ ├── catalog_list_bloc.dart
│ │ │ ├── catalog_list_event.dart
│ │ │ └── catalog_list_state.dart
│ │ └── catalog_list_screen.dart
│ └── widgets
│ └── catalog_name.dart
├── common
│ ├── config
│ │ └── config.dart
│ ├── exceptions
│ │ ├── exceptions.dart
│ │ ├── local_exceptions.dart
│ │ └── server_exceptions.dart
│ ├── helpers
│ │ └── helpers.dart
│ ├── platform
│ │ └── connectivity.dart
│ ├── utils
│ │ └── utils.dart
│ └── widgets
│ └── widgets.dart
├── data_sources
│ ├── product_local_data_provider.dart
│ ├── product_model.dart
│ └── product_remote_data_provider.dart
├── injections.dart
└── main.dart
mhadaily
Simple
Sales
Common
Catalog
E-commerce App
Presentation
Presentation
Screen
Logic Holder
BLoC
Widgets
Screen
Logic Holder
BLoC
mhadaily
Utils
Sales
Common
Catalog
E-commerce App
Domain
Presentation
Presentation
Presentation
mhadaily
Domain
Business logic
GetLatestNewsWithAuthorsUseCase
Sales
Common
Catalog
E-commerce App
Logic
Data
Presentation
Presentation
Presentation
Text
Remote Data Source
Local Data Source
Geo Location, ...
Raw Data
Raw Data
Raw Data
Models
Logic
Repositories
mhadaily
mhadaily
mhadaily
Majid Hajian
mhadaily
Slides/Codes
slides.com/mhadaily
SVG icons credited to undraw.co