When you want to use ‘continue’ in map

Answer by ChatGPT

In Swift, you cannot use continue statement inside the map function because map function traverses each element of an array and executes a closure for each element, and continue statement can only be used inside a loop. However, there is a way to skip a specific element and continue to the next iteration inside the map function. You can achieve this by using a conditional statement inside the closure to skip the element. For example, if you want to skip odd numbers and process only even numbers in an array, you can write the following code:

let numbers = [1, 2, 3, 4, 5, 6]
let evenNumbers = numbers.map { number in
    if number % 2 != 0 {
        return nil // skip the element by returning nil
    }
    return number * 2 // process even numbers by returning a value
}.compactMap { $0 } // remove nil values

print(evenNumbers) // [4, 8, 12]

In the code above, we return nil to skip odd numbers and return a value to process even numbers inside the map function. Then, we use the compactMap function to remove nil values and print the result.

Therefore, although you cannot use continue statement inside the map function, you can skip a specific element and continue to the next iteration by using a conditional statement inside the closure.

If you gets error “Generic parameter ‘ElementOfResult’ could not be inferred”, add return type to the map closure

from

let monthlyDividendViewModels = dividendResponse.monthlyDividends
                    .map { monthlySummary in
                        guard let month = monthlySummary.paymentMonth else {
                            return nil
                        }
                        return MonthlyDividendViewModel(sum: sum, stockDividends: stockDividends)
                    }
                    .compactMap { $0 }

to

let monthlyDividendViewModels = dividendResponse.monthlyDividends
                    .map { monthlySummary -> MonthlyDividendViewModel? in
                        guard let month = monthlySummary.paymentMonth else {
                            return nil
                        }
                        return MonthlyDividendViewModel(sum: sum, stockDividends: stockDividends)
                    }
                    .compactMap { $0 }

swift generate boilerplate code with automation using gyb

  1. gyb stands for Generate Your Boilerplate https://nshipster.co.kr/swift-gyb/
  2. its python
  3. python comment out: #
  4. Download gyb from https://github.com/apple/swift/blob/main/utils/gyb.py
  5. Add script to run gyb files into Build Phases or Add to shell script which runs gyb file to make Swift files.
find . -name '*.gyb' |                                               \
    while read file; do                                              \
        ${SCRIPT_DIR}/gyb.py --line-directive '' -o "${file%.gyb}" "$file"; \
    done
  1. Add “.gyb” to the name of Swift file you want to make
  2. Except for the swift code to put you need to prefix %, and you have to write “end” in the same depth of condition
enum AppEventComponent: String {
% for component in appEventComponents:
    % if component[0].isdigit():
        case _${component} = "${component}"
    % else:
        case ${component}
    % end
% end
}

4. set doesn’t have .sort() function. use sorted instead. Because Set does not have order, sorted() returns list.

appEventModules = sorted(appEventModules)

5. If you want to change cases in Swift to list for python, it’s convenient to use “replace regex” with “()”, “$1”.

6. To prevent duplication, use Enum. duplication in Enum makes compile error but Set doesn’t. If name is value, use “auto”

It’s a script which makes enum case if you put event name with format of “screen_button_action”

//
// This file is auto-generated.
// Run ./scripts/generate-gyb.bash
//
%{
    from enum import Enum, auto

    class Event(Enum):
        # Add new statistics events
        market_filter__main__enter = auto()

    class AppEventModule(Enum):
        add_buy = auto()

    class AppEventComponent(Enum):
        _1y = auto()

    class AppEventAction(Enum):
        enter = auto()

    appEventModules = set()
    appEventComponents = set()
    appEventActions = set()

    for event in Event:
        for (index, value) in enumerate(event.name.split('__')):
            if index == 0: appEventModules.add(value)
            elif index == 1: appEventComponents.add(value)
            elif index == 2: appEventActions.add(value)

    for module in AppEventModule:
        appEventModules.add(module.name)

    for component in AppEventComponent:
        if component.name.startswith('_'):
            appEventComponents.add(component.name[1:])
        else:
            appEventComponents.add(component.name)

    for action in AppEventAction:
        appEventActions.add(action.name)

    appEventModules = sorted(appEventModules)
    appEventComponents = sorted(appEventComponents)
    appEventActions = sorted(appEventActions)
}%
import Foundation

enum AppEventModule: String {
% for module in appEventModules:
    case ${module}
% end
}

enum AppEventComponent: String {
% for component in appEventComponents:
    % if component[0].isdigit():
        case _${component} = "${component}"
    % else:
        case ${component}
    % end
% end
}

enum AppEventAction: String {
% for action in appEventActions:
    case ${action}
% end
}

What is “==” when it’s reference type

That is what is written in equatable protocol’s method.

static func == (lhs: A, rhs: A) -> Bool {
}

To use “==” that type always needs to implement equatable.

For struct, if that type has only equatable properties, just writing “: equatable” makes the struct equatable. But for class, event though it only has equatable properties, you needs to implement “==” method to implement equatable.

How to not wait await function

Use Task

Task {
                self.userActionsCount = try await UsersMeActionsCountRepository().count()
                UserActionManager.shared.userActionsCount = userActionsCount
                self.updateTitleAndObserve()
            }
            
            let result = try await EvaluationRepository().videoOrWebtoonContents(page: 0, size: size, unrated: false, isVideo: true) // <- this line executes not waiting UsersMeActionsCountRepository().count() function

Reference: https://stackoverflow.com/a/69846696

  • But here’s a pitfall
do {
      Task {
        self.userActionsCount = try await UsersMeActionsCountRepository().count() //If a error occurs here, it's not catched 
        UserActionManager.shared.userActionsCount = userActionsCount
        self.updateTitleAndObserve()
      }
       
      let result = try await EvaluationRepository().videoOrWebtoonContents(page: 1, size: size, unrated: false, isVideo: true)
      nextPage = result.nextPage
      let contents = result.cells
      contentActionCells = contents
      cellViewModels = cellViewModelsFromContents(contents)
    } catch {
      self.error = error 
    }

optional 변수에 대한 flatMap

  1. 왜 flatMap이지? flatMap은 flat하게 만드는 기능이 있는 거 아니야?
    1. 맞음.
    2. 근데 flatMap에 또다른 기능이 있음. array가 아닐 때.
      1. optional한 애를 인자로 받아서, 또 안에서 연산을 통해 optional한 애로 리턴해줄 때임.
      2. optional하지 않다면(nil로 리턴해줄 일이 없다면) 그냥 map을 쓰면 됨. 근데 이때 유의점은 그냥 map을 써도 optional이긴 함
        1. 그럼 compactMap? 은 optional한 변수 자체에 대해서는 없는 기능임.
    3. 잠깐만 deprecated 됐던거 아니야?
      1. deprecated된건 compactMap처럼 optional element의 array일 때 사용되는 거. 이때는 flatMap을 써야 함.