Flutter installation

This is a summary of what this YouTube video explains. Recommended. Youtube - Install Flutter macOS - Derek Banas

1. Install Flutter

brew install --cask flutter
flutter doctor
flutter upgrade

2. Optional: Install Android Studio

Install Android studio if you plan to target Android devices.
Download latest Android Studio from https://developer.android.com/studio and install Flutter plugin
Settings > Plugins > Flutter In Settings > System Settings
  • In SDK Platforms tab, make sure at least one API level is installed. This is needed for running apps.
  • In SDK Tools tab, make sure Android SDK Command Line Tools (latest) is installed. This is needed by flutter.
  • Set ANDROID_HOME Get your Android sdk location from Android Studio
    echo export ANDROID_HOME="/Users/ignacio/Library/Android/sdk" >> ~/.zshrc
    If needed, restart terminal to get settings applied.
In recent versions of Android Studio jre directory has been renamed to jbr so Flutter gets confused. To solve this we create a link. (Thanks to this Stackoverflow answer!)
cd /Applications/Android\ Studio.app/Contents
ln -s jbr jre
Now Flutter show be able to fully recognize Android, we run the doctor:
flutter doctor -v
flutter doctor --android-licenses
Last step on Android Studio: The first time we create a Flutter project we need to setup Flutter SDK path. We get it from flutter doctor -v command. Just look for a line like this:
• Flutter version 3.7.1 on channel stable at /opt/homebrew/Caskroom/flutter/3.7.0/flutter

3. Optional: Install CocoaPods

I suspect this is only required if you target iOS devices.
brew install cocoapods

4. Final check

At the end we should have everything setup. Run the doctor:
flutter doctor -v

Aggregate target to create xcframework

It has been a while I create an xcframework from scratch so these is hopefully a all-mighty script that will work with a framework target.
Create an aggregate target and add a "Run Script" like below.
set -e

N_SCHEME_NAME=MySchemeNameHere
N_BUILD_DIR="build"
N_IOS_XCARCHIVE="${N_BUILD_DIR}/${N_SCHEME_NAME}-iphoneos.xcarchive"
N_SIM_XCARCHIVE="${N_BUILD_DIR}/${N_SCHEME_NAME}-iphonesimulator.xcarchive"
N_XCFRAMEWORK="${N_BUILD_DIR}/${N_SCHEME_NAME}.xcframework"

rm -rf "$N_BUILD_DIR"

echo "🚀 Building $N_IOS_XCARCHIVE"
xcodebuild archive \
    -scheme "$N_SCHEME_NAME" \
    -archivePath "$N_IOS_XCARCHIVE" \
    -sdk iphoneos \
    BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
    SKIP_INSTALL=NO

echo "🚀 Building $N_SIM_XCARCHIVE" 
xcodebuild archive \
    -scheme "$N_SCHEME_NAME" \
    -archivePath "$N_SIM_XCARCHIVE" \
    -sdk iphonesimulator \
    BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
    SKIP_INSTALL=NO
 
echo "🚀 Building $N_XCFRAMEWORK"
xcodebuild -create-xcframework \
    -framework "${N_IOS_XCARCHIVE}/Products/Library/Frameworks/${N_SCHEME_NAME}.framework" \
    -framework "${N_SIM_XCARCHIVE}/Products/Library/Frameworks/${N_SCHEME_NAME}.framework" \
    -output "${N_XCFRAMEWORK}"

echo "🚀🟢 Build SUCCESS"

Hope it helps.

Convert old FAT .framework to .xcframework

Sometimes when working with old fat frameworks they can cause warnings in Xcode. By using lipo and xcodebuild they can be converted to the new format and Xcode will not complain anymore!

See Usage at the bottom of the script

Note:

In below script bash is required because I use automatic word splitting for REMOVE_FOR_IOS and REMOVE_FOR_SIM expansion. Using word splitting is probably no the best option however simplicity wins here. Specially in a script that is supposed to be executed only a couple of times in the time of our lifes.

#! /bin/bash
# Bash (not zsh) is required for word splitting for REMOVE_FOR_IOS and REMOVE_FOR_SIM inside make_part function

make_part() {
    FRAMEWORK_NAME=${1%.framework}
    TEMP_DIR=$2
    ARCHS_TO_REMOVE=$3

    rm -rf "$TEMP_DIR"
    mkdir $TEMP_DIR
    cp -R "${FRAMEWORK_NAME}.framework" "$TEMP_DIR"
    pushd "${TEMP_DIR}/${FRAMEWORK_NAME}.framework" > /dev/null || return
    #xcrun lipo -info "${FRAMEWORK_NAME}"
    mv "${FRAMEWORK_NAME}" "${FRAMEWORK_NAME}_original" 
    # xcrun lipo "${FRAMEWORK_NAME}_original" -remove i386 -remove x86_64 -output "${FRAMEWORK_NAME}"
    xcrun lipo "${FRAMEWORK_NAME}_original" $ARCHS_TO_REMOVE -output "${FRAMEWORK_NAME}"
    xcrun lipo -info "${FRAMEWORK_NAME}"
    rm "${FRAMEWORK_NAME}_original"
    popd > /dev/null
}

make_xcframework () {
    FRAMEWORK_NAME=${1%.framework}
    REMOVE_FOR_IOS=$2
    REMOVE_FOR_SIM=$3
    DIR_IOS=temp_ios
    DIR_SIM=temp_sim
    
    echo "Processing framework: ${FRAMEWORK_NAME}.framework"

    echo "Creating ios part"
    make_part "$FRAMEWORK_NAME" "$DIR_IOS" "$REMOVE_FOR_IOS"

    echo "Creating simulator part"
    make_part "$FRAMEWORK_NAME" "$DIR_SIM" "$REMOVE_FOR_SIM"

    echo "Creating xcframework"
    rm -rf "${FRAMEWORK_NAME}.xcframework"
    xcodebuild -create-xcframework \
        -framework "${DIR_IOS}/${FRAMEWORK_NAME}.framework" \
        -framework "${DIR_SIM}/${FRAMEWORK_NAME}.framework" \
        -output "${FRAMEWORK_NAME}.xcframework"
    
    rm -rf "$DIR_IOS" "$DIR_SIM"
    #tree "${FRAMEWORK_NAME}.xcframework"
    echo 'OK!'
}

# First parameter: name of the framework to convert
# Second parameter: architectures to remove for ios (device) in the form: "-remove ABC -remove XYZ"
# Third parameter: architectures to remove for ios simulator  in the form: "-remove ABC -remove XYZ"

Examples: 
make_xcframework MyOldFramework.framework "-remove i386 -remove x86_64" "-remove armv7 -remove arm64"
make_xcframework OtherFramework.framework "-remove i386 -remove x86_64" "-remove armv7 -remove arm64"
make_xcframework AnotherFramework.framework "-remove i386 -remove x86_64" "-remove armv7s -remove armv7 -remove arm64"

Using periphery

Periphery is a promissing looking tool for finding dead code!. I have unsuccessfully tried it the past and finally I found a way to build my project correctly with it.

Short explanation, CODE_SIGNING_ALLOWED=NO is set by default however in some xcode projects some files (usually old .frameworks) might have the "Code Sign on Copy" setting set to YES (on Target > Build Phases > Copy files). This kind of projects need CODE_SIGNING_ALLOWED=YES.

Below is an example of my .periphery.yml file:

project: MyProject.xcodeproj
retain_objc_accessible: true
retain_public: true
schemes:
- MyScheme
targets:
- MyTargetTests
- MyTarget
verbose: true
clean_build: true
build_arguments:
- -destination
- platform="iOS Simulator,name=iPhone 8,OS=latest"
- CODE_SIGNING_ALLOWED="YES"

Run it

periphery scan --config .periphery.yml
Default ouput format is xcode which is good for xcode integration and for humans that like to read the terminal. Probably json output is probably easier for integrate with other services like Sonarqube custom issues, etc :)

SwiftUI Notes

I will add a bunch of little notes as I read all the tutorials in the internet. (Yep, ALL the tutorials in the internet)

Ignoring Safe Area

Apparently ignoresSafeArea and edgesIgnoringSafeAreaboth do the same.
let colors = [Color.init(red: 0.1, green: 0.1, blue: 0.6), Color.blue]
LinearGradient(gradient: Gradient(colors: colors), startPoint: .topLeading, endPoint: .bottomTrailing)

// This is the preferred way: `@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)`
.ignoresSafeArea(SafeAreaRegions.all, edges: [Edge.Set.top, .bottom])

// Header says deprecated: `@available(iOS, introduced: 13.0, deprecated: 100000.0, message: "Use ignoresSafeArea(_:edges:) instead.")`
.edgesIgnoringSafeArea(Edge.Set.all)

'kSecUseNoAuthenticationUI' is deprecated: first deprecated in iOS 9.0 - Use kSecUseAuthenticationUI instead.

This is a very old warning found in some old versions of some old libraries.
I just copy the link of of what it should a fix should look like: AWSUICKeyChainStore fix
- query[(__bridge __strong id)kSecUseNoAuthenticationUI] = (__bridge id)kCFBooleanTrue;
+ query[(__bridge __strong id)kSecUseAuthenticationUI] = (__bridge id)kSecUseAuthenticationUIFail;

Get ipa file name from xcodeproject PRODUCT_NAME

PRODUCT_NAME=$(xcrun xcodebuild -showBuildSettings -scheme "${SCHEME}" -configuration "${CONFIGURATION}" | grep ' PRODUCT_NAME ' | awk '{ print $3 }')

echo "PRODUCT_NAME=${PRODUCT_NAME}"
CONFIGURATION is usually RELEASE or DEBUG SCHEME is the thingy to the left of simulator or device :) Now this PRODUCT_NAME can be used to search the ipa file after xcodebuild finished building:
IPA_FILENAME=$(find "$ARCHIVE_DIR" -type f -name "${PRODUCT_NAME}.ipa")

This work is licensed under BSD Zero Clause License | nacho4d ®