​​В ряде случаев приложение, написанное на языке Objective-C, для улучшения функционирования и расширения его возможностей, приходится переписывать на Swift. Осуществление перевода на язык Swift сразу всего кода не всегда рационально, гораздо разумным представляется переписывание  класса за классом. При этом сборка становится гибридной и соответственно необходимо делать так, чтобы все объявления были видимы в коде на обоих языках. 

Ввиду того, что крупные проекты содержат множество файлов понять как последние связаны друг с другом может быть затруднительным. В этой связи прежде чем приступить к переводу логичным явилось бы создание скриптов, которые позволят визуализировать зависимости между файлами Objective-C и сделать анализ проекта более удобным и понятным. 

Данная работа посвящена созданию скриптов, помогающих оценивать зависимости между файлами Objective-C приложения перед переписыванием на Swift  на основании:  

А) подсчета числа Objective-C файлов с расширением .m и .h;

B) построения графа зависимостей между этими файлами;

C) создания таблицы заполненной файлами в порядке возрастания числа зависимостей, определяющеего очередность перевода файлов на Swift.

В туториале предполагается  использование языка программирования Python и нескольких библиотек - таких как networkx для работы с графами, matplotlib для визуализации графа и openpyxl для сохранения результатов в таблице Excel.

Чтобы запустить скрипт на языке Python, который считает файлы следует выполнить следующие шаги:

  • Установить интерпретатор Python, если он еще не установлен (можно с помощью brew команды: brew install python).

  • Открыть терминал и перейти в папку проекта используя команду cd и указав путь к папке. Например, если вы хотите создать файл в папке Documents, выполните команду cd ~/Documents.

  • Создайте файл с помощью команды touch и указав имя файла с расширением .py. Например, чтобы создать файл counts.py, выполните команду touch counts.py.

  • Откройте созданный файл в текстовом редакторе.

  • Скопируйте код в созданный файл counts.py.

Реализация скрипта (counts.py), определяющего и подсчитывающего количество файлов с расширением .m и .h в директории проекта, исключая папку Pods и все ее подпапки:
//counts.py

 import os
 
# Путь к директории проекта
project_path = "/Users/Anon/Documents/Projects/YOUR PROJECT"
 
# Инициализировать счетчики
m_files_count = 0
h_files_count = 0
 
# Пройти по всем файлам в директории проекта и ее поддиректориям
for root, dirs, files in os.walk(project_path):
	# Если папка 'Pods' входит в список подпапок текущей директории, исключить ее из списка обрабатываемых папок
	if 'Pods' in dirs:
    	dirs.remove('Pods')
	for file in files:
    	# Определить расширение файла
    	ext = os.path.splitext(file)[1]
    	# Если расширение файла .m, увеличить счетчик m_files_count
    	if ext == ".m":
        	m_files_count += 1
    	# Если расширение файла .h, увеличить счетчик h_files_count
    	elif ext == ".h":
        	h_files_count += 1
 
# Вывести результаты
print(f"Количество файлов .m: {m_files_count}")
print(f"Количество файлов .h: {h_files_count}")

При этом необходимо заменить путь к директории проекта в строке 4 на путь к вашей директории проекта.

  • Сохраните файл, нажав на кнопку "Сохранить" или используя комбинацию клавиш Ctrl+O. Убедитесь, что выбрано расширение .py.

  • Запуститите скрипт командой “python3 counts.py

Чтобы запустить скрипт на языке Python, который проходит по зависимостям Objective-C файлов, следует выполнить следующие шаги:

Перед запуском скрипта вам понадобится установить несколько библиотек для этого:

  1. Откройте терминал и убедитесь, что установлен pip, выполнив следующую команду: pip –version. Если получено сообщение об ошибке, установить pip, следуя инструкциям на официальном сайте: https://pip.pypa.io/en/stable/installation/

  2. Установите matplotlib, выполнив команду: pip install matplotlibscipy, реализовав команду: pip install scipy, networkx, исполнив  команду: pip install networkx

  • Сохраните скрипт в файле с расширением .py. Например, можно создать файл с именем dependencies.py

Реализация скрипта(dependencies.py), осуществляющего построение графа зависимостей между зафиксированными файлами.
import os
import re
import networkx as nx
import matplotlib.pyplot as plt
 
# Функция для поиска зависимостей между файлами Objective-C
def find_dependencies(file):
    with open(file, 'r') as f:
    	content = f.read()
    	dependencies = set()
    	headers = re.findall(r'#import\s+"(.*)"', content)
   	 for header in headers:
       	 # Если зависимость находится в папке 'Pods', пропустить ее
       	 if 'Pods' in header:
           	 continue
        	dependencies.add(header)
   	 return dependencies
 
# Путь к директории проекта
project_path = "/Users/ПУТЬ_К_ПРОЕКТУ"
 
# Создать пустой граф
graph = nx.DiGraph()
 
# Пройти по всем файлам в директории проекта и ее поддиректориям
for root, dirs, files in os.walk(project_path):
    # Если папка 'Pods' входит в список подпапок текущей директории, исключить ее из списка обрабатываемых папок
    if 'Pods' in dirs:
    	dirs.remove('Pods')
    for file in files:
   	 # Определить расширение файла
    	ext = os.path.splitext(file)[1]
   	 # Если расширение файла .m или .h, добавить узел в граф
   	 if ext in [".m", ".h"]:
        	file_path = os.path.join(root, file)
        	node_name = os.path.splitext(os.path.basename(file_path))[0]
        	graph.add_node(node_name)
       	 # Найти все зависимости между файлами Objective-C
        	dependencies = find_dependencies(file_path)
       	 for dependency in dependencies:
            	dependency_path = os.path.join(root, dependency)
           	 # Если зависимость также является файлом Objective-C, добавить ребро в граф
           	 if os.path.exists(dependency_path) and os.path.splitext(dependency_path)[1] in [".h", ".m"]:
         	       dependency_name = os.path.splitext(os.path.basename(dependency_path))[0]
                	graph.add_edge(node_name, dependency_name)
 
# Нарисовать граф
plt.figure(figsize=(40,40))
pos = nx.spring_layout(graph, k=0.05, seed=123)
nx.draw_networkx_nodes(graph, pos, node_size=10)
nx.draw_networkx_edges(graph, pos, width=0.2, arrowsize=1, arrowstyle='->', arrows=True)
 
# Сохранить граф в файл GraphML
nx.write_graphml(graph, "dependencies.graphml")
 
# Показать граф
plt.axis('off')
plt.show()

Пожалуйста, не забудьте указать путь к директории проекта в 20-ой строчке.

  • Откройте терминал.

  • Перейти в каталог, где сохранен файл со скриптом. Например, если файл находится в папке Documents, нужно выполнить команду cd Documents в терминале.

  • Запустить скрипт, выполнив команду python3 dependencies.py.

После того как скрипт завершит работу вы увидете в папке проекта файл dependencies.graphml. Его можно открыть с помощью программ для визуализации графов, например, Gephi, в котором для открытия dependencies.graphml, необходимы следующие шаги:

  • Скачать и открыть Gephi(https://gephi.org/users/download/).

  • Нажать на кнопку "Open Graph File" на верхней панели инструментов.

  • Выбрать созданный файл dependencies.graphml в диалоговом окне открытия файла и нажать кнопку "Open".

До применения алгоритма
До применения алгоритма
  • Для улучшения визуализации объекта, после загрузки файла dependencies.graphml в Gephi, можно изменять отображение графа, применять различные алгоритмы раскладки, изменять цвет, размер узлов и ребер, и т.д.

после применения алгоритма Fruchterman Reingold
после применения алгоритма Fruchterman Reingold

Создание таблицы заполненной файлами в порядке возрастания числа зависимостей(exel.py)

Перед запуском скрипта вам понадобится установить библиотеку openpyxl для этого, для чего можно использовать менеджер пакетов pip(pip install openpyxl).

Скрипт (exel.py)
import os
import re
import openpyxl
 
# Функция для поиска зависимостей между файлами Objective-C
def find_dependencies(file):
    with open(file, 'r') as f:
    	content = f.read()
    	headers = re.findall(r'#import\s+"(.*)"', content)
   	 return headers
 
# Путь к директории проекта
project_path = "ПУТЬ_К_ПРОЕКТУ"
 
# Инициализировать счетчики
dependencies = {}
m_files_count = 0
h_files_count = 0
 
# Пройти по всем файлам в директории проекта и ее поддиректориям
for root, dirs, files in os.walk(project_path):
    # Если папка 'Pods' входит в список подпапок текущей директории, исключить ее из списка обрабатываемых папок
    if 'Pods' in dirs:
   	 dirs.remove('Pods')
    for file in files:
   	 # Определить расширение файла
    	ext = os.path.splitext(file)[1]
   	 # Если расширение файла .m, добавить файл в словарь dependencies и увеличить счетчик m_files_count
   	 if ext == ".m":
            file_path = os.path.join(root, file)
        	dependencies[file] = len(find_dependencies(file_path))
        	m_files_count += 1
   	 # Если расширение файла .h, увеличить счетчик h_files_count
   	 elif ext == ".h":
        	h_files_count += 1
 
# Создать таблицу Excel и заполнить ее именами файлов в порядке возрастания числа зависимостей
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "File Names"
ws.cell(row=1, column=1, value="File Name")
ws.cell(row=1, column=2, value="Dependencies")
row = 2
for file, dep_count in sorted(dependencies.items(), key=lambda x: x[1]):
	ws.cell(row=row, column=1, value=file)
	ws.cell(row=row, column=2, value=dep_count)
	row += 1
 
# Сохранить таблицу в файл Excel
wb.save("file_names.xlsx")
 

При этом заменить путь к директории проекта в строке 7 на путь к вашей директории проекта.

Спасибо за чтение этого туториала! Удачи в ваших проектах и дальнейшем развитии в области iOS-разработки! Подписывайтесь на мой канал с авторским контентом SwiftyGroup

Комментарии (0)