Slide 1

Slide 1 text

SwiftSyntax UIKit SwiftUI 用 202 4 . 1 0 . 23 @dsxsxsxs © LINE Digital Frontier Corporation

Slide 2

Slide 2 text

自己 人 日 LINE Digital Frontier VTuber

Slide 3

Slide 3 text

SwiftUI 行 子

Slide 4

Slide 4 text

⾒ UIKit 250/266 percentage: 98.5% SwiftUI 16/266 percentage: 1.5%

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

import 文 View

Slide 7

Slide 7 text

import 文 🤔 水 ❌ ❌ import SwiftUI import SwiftUI import SwiftUI import SwiftUI import SwiftUI import SwiftUI import SwiftUI import SwiftUI import SwiftUI import SwiftUI import SwiftUI import SwiftUI import SwiftUI import SwiftUI import SwiftUI import SwiftUI import SwiftUI import SwiftUI

Slide 8

Slide 8 text

UI SwiftUI UIKit 文

Slide 9

Slide 9 text

class HogeView: UIView { struct FugaView: View {

Slide 10

Slide 10 text

Regexp?

Slide 11

Slide 11 text

No

Slide 12

Slide 12 text

文 行 白 where 文 🤯

Slide 13

Slide 13 text

SwiftSyntax

Slide 14

Slide 14 text

InheritanceClause

Slide 15

Slide 15 text

CView BView AView UIView

Slide 16

Slide 16 text

n 目 見 SwiftUI, UIKit

Slide 17

Slide 17 text

func getSwiftFiles(in directory: URL) -> [URL] { var fileURLs: [URL] = [] let fileManager = FileManager.default if let enumerator = fileManager.enumerator(at: directory, includingPropertiesForKeys: nil) { for case let fileURL as URL in enumerator { if fileURL.pathExtension == "swift" { fileURLs.append(fileURL) } } } return fileURLs } .swift AI 生 👍

Slide 18

Slide 18 text

func declSyntaxs(of fileURL: URL) async throws -> [DeclSyntaxProtocol] { var decls: [DeclSyntaxProtocol] = [] let data = try Data(contentsOf: fileURL) let sourceFile = Parser.parse(source: String(data: data, encoding: .utf8)!) for statement in sourceFile.statements { if let classDecl = statement.item.as(ClassDeclSyntax.self) { decls.append(classDecl) } else if let structDecl = statement.item.as(StructDeclSyntax.self) { decls.append(structDecl) } } return decls } SwiftSyntaxParser 文

Slide 19

Slide 19 text

let initialUIKitTypes: Set = [ "UIView", "UIButton", "UILabel", "UITableView", "UICollectionView", "UIScrollView", "UIImageView", "UIStackView", "UITextField", "UITextView", "UIBarButtonItem", "UINavigationBar", "UIViewController", "UINavigationBar", "UINavigationController", "UITabBarItem", "UITabBar", "UITabBarController", "UIHostingController", "UICollectionViewController", "UITableViewController", "UIControl", "UIPickerView", "UIDatePicker", "UISwitch", "UIProgressView", "UIActivityIndicatorView", "UIStepper", “UISegmentedControl" ] let initialSwiftUITypes: Set = [ "View", "Text", "Button", "Image", "List", "VStack", "HStack", "ZStack", "LazyVStack", "LazyHStack", "LazyZStack", "ScrollView", "NavigationView", "Form", "Group", "Section", "Spacer", "Divider", "Picker", "Slider", "Stepper", "Toggle", "TextField", "TextEditor", "Shape", "Path", "Rectangle", "RoundedRectangle", "Circle", "Ellipse", "Capsule", "GeometryReader", "Color", "ClipShape", "Mask" ] AI 生 👍

Slide 20

Slide 20 text

if let classDecl = decl.as(ClassDeclSyntax.self), let inheritanceClause = classDecl.inheritanceClause { let className = classDecl.name.text let typeThatHit = inheritanceClause .inheritedTypes.map { $0.type.trimmed.description } .first { uiKit.contains($0) } if typeThatHit != nil { uiKitTypes.insert(className) print("🔴 run \(run), found \(className) in \(fileURL.lastPathComponent)") } } UIKit

Slide 21

Slide 21 text

if let structDecl = decl.as(StructDeclSyntax.self), let inheritanceClause = structDecl.inheritanceClause { let structName = structDecl.name.text let typeThatHit = inheritanceClause .inheritedTypes.map { $0.type.trimmed.description } .first { swiftUI.contains($0) } if typeThatHit != nil { swiftUITypes.insert(structName) print("🟢 run \(run), found \(structName) in \(fileURL.lastPathComponent)") } } SwiftUI ⾒

Slide 22

Slide 22 text

func analyzeFiles(swiftFiles: [URL], uiKit: Set, swiftUI: Set, run: Int) -> (uiKitTypes: Set, swiftUITypes: Set) { var uiKitTypes: Set = [] var swiftUITypes: Set = [] for fileURL in swiftFiles { do { let decls = try await declSyntaxs(of: fileURL) for decl in decls { if /* লུ */ { uiKitTypes.insert(className) print("🔴 run \(run), found \(className) in \(fileURL.lastPathComponent)") } else if /* লུ */ { swiftUITypes.insert(structName) print("🟢 run \(run), found \(structName) in \(fileURL.lastPathComponent)") } } } catch { print("Failed to parse \(fileURL): \(error)") } } return (uiKitTypes, swiftUITypes) }

Slide 23

Slide 23 text

func analyzeProject(at directory: URL) { let start = Date().timeIntervalSince1970 let swiftFiles = getSwiftFiles(in: directory) var uiKitTypes: Set = [] var swiftUITypes: Set = [] print(“😡 \(swiftFiles.count) files") var run = 0 var uikit: Set = initialUIKitTypes var swiftUI: Set = initialSwiftUITypes while !uikit.isEmpty || !swiftUI.isEmpty { run += 1 let (foundUIKit, foundSwiftUI) = analyzeFiles(swiftFiles: swiftFiles, uiKit: uikit, swiftUI: swiftUI, run: run) uiKitTypes.formUnion(foundUIKit) swiftUITypes.formUnion(foundSwiftUI) uikit = foundUIKit swiftUI = foundSwiftUI } print("✅ no more childs found. finish.") 見

Slide 24

Slide 24 text

let totalCount = uiKitTypes.count + swiftUITypes.count let uiKitPercentage = totalCount > 0 ? (Double(uiKitTypes.count) / Double(totalCount)) * 100 : 0 let swiftUIPercentage = totalCount > 0 ? (Double(swiftUITypes.count) / Double(totalCount)) * 100 : 0 print("UIKit: \(uiKitTypes.sorted())") print("SwiftUI: \(swiftUITypes.sorted())") print("UIKit \(uiKitTypes.count)/\(totalCount) percentage: \(uiKitPercentage)%") print("SwiftUI \(swiftUITypes.count)/\(totalCount) percentage: \(swiftUIPercentage)%") let end = Date().timeIntervalSince1970 print("🕒 Time taken: \(end - start) seconds") 力

Slide 25

Slide 25 text

UIKit 316/390 percentage: 81.02564102564102% SwiftUI 74/390 percentage: 18.974358974358974% 🕒Time taken: 116.3344030380249 seconds

Slide 26

Slide 26 text

Cache Concurrency 高

Slide 27

Slide 27 text

UIKit 316/390 percentage: 81.02564102564102% SwiftUI 74/390 percentage: 18.974358974358974% 🕒Time taken: 8.271837711334229 seconds

Slide 28

Slide 28 text

SwiftSyntax 文 UI AI 手 高 CI Slack

Slide 29

Slide 29 text

End Of doc.