<![CDATA[Xing in New Work Development on Medium]]> https://tech.new-work.se/tagged/xing?source=rss----35cb8c78d3cf--xing https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png Xing in New Work Development on Medium https://tech.new-work.se/tagged/xing?source=rss----35cb8c78d3cf--xing Medium Fri, 13 Sep 2024 20:32:26 GMT <![CDATA[How to use Swift Package Manager products from Cocoapods]]> https://tech.new-work.se/how-to-use-swift-package-manager-products-from-cocoapods-96f225a12a20?source=rss----35cb8c78d3cf--xing https://medium.com/p/96f225a12a20 Fri, 19 May 2023 09:48:48 GMT 2023-05-19T09:48:48.814Z Roughly one year ago, New Work SE’s iOS Platform Team started a proof of concept about how we could move from our existing XING iOS App, based in over one hundred Cocoapods internal libraries and other 15 external dependencies, into a clean and modern project based only in SwiftPM packages.

If you’re interested, I explained how we simplified the previous graph dependency in a previous article here in order to tackle this project in an easier way.

How to control your iOS dependencies

Now, I want to share with you, how the process of migration to SwiftPM can be easier than expected if you are an iOS developer who works in a large iOS project with:

  • a Cocoapods setup with large number of Development Pods
  • a local Swift Package where you want to move these Pods as a targets
Photo by Michał Parzuchowski on Unsplash

Which was the problem we wanted to solve?

We started defining our migration plan, which was created by using our jungle tool in a Xcode Playground, to explore the dependency graph of the project and build the required steps for that migration in a way that only modules that were dependant of already migrated modules can be also migrated. Easy.

But then, only a few days after we started the migration process, we arrived to this kind of situation you see in the image below where only one or a reduced number of modules were used in our still not migrated Pods.

Can we use our already migrated modules in the SPM Package in our still active Pods?

We asked ourselves, “Is there a way to start consuming these new migrated modules into our legacy Cocoapods Modules setup without having duplicated modules in both package managers?”

Also, we wanted to start having results as soon as possible in the final iOS App. Integrating some of these migrated libraries in the final binary would allow us to start monitoring the behaviour in our APM solution and enjoying the benefits without waiting for the complete migration and (maybe) having a hard switch to SPM process.

Our first approach was to use this pod_install hook that configures a Swift Package Manager dependency into an existing Xcode Project (the ones that Cocoapods creates for us). But then, this error was happening when we built the app:

How we solved this problem?

We didn’t want to move to SwiftPM and create a new dependency with the Xcodeproj library that is used by Cocoapods. So, why not using the new Xcodeproj (lowercased P) Swift version by Tuist to configure our Pod Xcode Project and have an easier way to properly configure these Xcode Projects.

We defined 2 differents commands for local and remote Swift packages:

import XcodeProj
import PathKit
import ArgumentParser

struct AddLocalPackageCommand: ParsableCommand {
static var configuration = CommandConfiguration(
commandName: "addLocal",
abstract: "Injects a local SPM Package"
)

@Option(help: "The Pod project directory.")
var projectPath: String

@Option(help: "The SwiftPM package directory.")
var spmPath: String

@Option(help: "The product from that package to be injected.")
var product: String

@Option(help: "The target to be configured with that dependency")
var targetName: String

func run() throws {
let projectPath = Path(projectPath)
let spmPath = Path(spmPath)
let xcodeproject = try XcodeProj(path: projectPath)
let pbxproj = xcodeproject.pbxproj
let project = pbxproj.projects.first
_ = try project?.addLocalSwiftPackage(path: spmPath, productName: product, targetName: targetName)
try xcodeproject.write(path: projectPath)}
}

struct AddRemotePackageCommand: ParsableCommand {
static var configuration = CommandConfiguration(
commandName: "addRemote",
abstract: "Injects a remote SPM Package"
)

@Option(help: "The Pod project directory.")
var projectPath: String

@Option(help: "The SwiftPM package URL.")
var spmURL: String

@Option(help: "The product from that package to be injected.")
var product: String

@Option(help: "The exact version to be used.")
var version: String

@Option(help: "The target to be configured with that dependency")
var targetName: String

func run() throws {
let projectPath = Path(projectPath)
let xcodeproject = try XcodeProj(path: projectPath)
let pbxproj = xcodeproject.pbxproj
let project = pbxproj.projects.first
_ = try project?.addSwiftPackage(repositoryURL: spmURL, productName: product, versionRequirement: .exact(version), targetName: targetName)
try xcodeproject.write(path: projectPath)}
}

This way, we could include this Swift CLI tool (we called XcodeSPMI by obvious reasons) in our Package and use it after the Pod installation.

import PackageDescription

let package = Package(
name: "libraries",
products: [
.library(name: "FeatureB", type: .dynamic, targets: ["FeatureB"]),
.executable(name: "XcodeSPMI", targets: ["XcodeSPMI"]),
],
dependencies: [
.package(url: "https://github.com/tuist/XcodeProj.git", .upToNextMajor(from: "8.9.0")),
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.2"),
],
targets: [
.target(name: "FeatureB", path: "FeatureB"),
.executableTarget(
name: "XcodeSPMI",
dependencies: [
.product(name: "XcodeProj", package: "XcodeProj"),
.product(name: "ArgumentParser", package: "swift-argument-parser")
])

]
)

Once we removed the dependency of FeatureB in the FeatureA’s .podspec file, this is how we inject (as a post_integrate step in the Podfile) the SPM dependency in the Cocoapods project using the previous .executable product from our Swift Package:

post_integrate do |installer|

# FeatureB
featureB_dependant = ["FeatureA"]

puts "Injecting FeatureB SPM framework into ..."
featureB_dependant.each do |project|
puts " #{project}"
`swift run --package-path libraries XcodeSPMI addLocal --project-path Pods/#{project}.xcodeproj --spm-path ../libraries/ --product FeatureB --target-name #{project}`
end
end

Is this enough? It’s for .binaryTargets but not for regular .targets as the one you can see in the example (FeatureB).

For .binaryTargets, which is the current solution we’re using for these shared modules, we are creating the .xcframework artifacts by using swift-create-xcframework.

In order to been able to remove the No such module ‘FeatureB’ error from your build log for plain .targets, there is an extra step needed during the module step. Looking at the logs we found something was not completely provided to the swift-frontend tool. The missing part was this variable you can find in the Build Setting documentation from Apple (SWIFT_INCLUDE_PATHS) which should also contain the directory where other modules can be found during that building stage.

Import Paths
Setting name: SWIFT_INCLUDE_PATHS
A list of paths to be searched by the Swift compiler for additional Swift modules.

As we can change the build settings for our development pods, that’s what we need to include in our .podspec file:

Pod::Spec.new do |s|
s.name = 'FeatureA'
s.version = '1.0.0'
s.author = 'Oswaldo Rubio'
s.license = 'commercial'
s.homepage = "https://github.com/osrufung/UsingSPMFromCPDemo"
s.source = { git: 'https://github.com/osrufung/UsingSPMFromCPDemo' }
s.summary = "#{s.name} – Version #{s.version}"
s.ios.deployment_target = '15.0'
s.swift_version = '5.7'
s.source_files = "Sources/**/*.swift"
# This resolves the missing SWIFT_INCLUDE_PATHS variable
s.pod_target_xcconfig = { 'SWIFT_INCLUDE_PATHS' => '$(inherited) ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)'}
end

Conclusion

Having this SPM injection into Cocoapods projects allow us to increase the number of Integrated modules (modules that are being linked with the final app) before finishing migrate the pending modules.

We’re having multiple benefits of this “hack”:

  • being able to remove from Cocoapods modules that are already migrated in SPM.
  • also injecting the dependency as a .binaryTarget in .xcframework format reduces the total build time spent locally and time resources and credits in the CI side.
  • Foundational modules shared in both package managers without any kind of library duplication.

We expect to finish this migration process during this year. This is the current migration state right now and I hope to share my thoughts about the whole migration project once we finished with you.

I mentioned before we had over one hundred internal modules in our Podfile, and that’s how the migration process looks today (mid of May 2023). Some of the modules have been deleted (because some features were removed), and other ones have been migrated but still integration in the app is still not possible because some of the modules that are dependant on them are still in the pending to be migrated list.

Visual representation with percentage of migrated, integrated and pending to migrate modules
current SPM migration status in our XING iOS project

If you want to play yourself, the issue can be reproduced and is already solved in this demo project along with a really tiny SwiftPM injector .executableTarget based on XcodeProj. I hope this can be useful for your projects and please share your thoughts or problems in the comments or in the github repository.


How to use Swift Package Manager products from Cocoapods was originally published in New Work Development on Medium, where people are continuing the conversation by highlighting and responding to this story.

]]>
<![CDATA[Seniority Balance in Software Engineering Teams]]> https://tech.new-work.se/seniority-balance-in-software-engineering-teams-6973db8e1b38?source=rss----35cb8c78d3cf--xing https://medium.com/p/6973db8e1b38 Fri, 17 Mar 2023 14:02:37 GMT 2023-03-17T14:02:37.522Z In Software Engineering teams, seniority balance plays a crucial role in achieving delivery capacity, mentoring opportunities and capability, employee retention, costs, and sustainability. A team with a good balance of seniority levels can provide several advantages, including a diverse range of skill sets, perspectives, and experience levels.

New Work, Portugal —Software Engineering Team

Delivery Capacity

Seniority balance is essential for the delivery capacity of Software Engineering teams. A team composed of only junior engineers may not have the necessary experience to handle complex tasks or to make significant decisions. On the other hand, a team composed of only senior engineers may lack a new perspective on topics and out of the box thinking to come up with innovative solutions to problems.

By having a mix of experience, a team can have the necessary expertise to handle complex tasks, while also having the energy and creativity to come up with new ideas. This balance can help ensure that projects are completed on time and within budget.

Mentoring Opportunities and Capability

Seniority balance also provides opportunities for mentoring and professional development. Senior engineers can serve as mentors to junior engineers, passing on their knowledge and expertise. This mentoring can help junior engineers learn new skills, gain confidence, and grow in their careers.

Additionally, senior engineers can learn from junior engineers as well and can pass on knowledge and experience, as it was once done with them. Junior engineers may have new ideas or fresh perspectives that can help senior engineers think outside the box and come up with innovative solutions.

Employee Retention

A team with a good seniority balance can also help with employee retention. Junior engineers may leave if they feel they are not being challenged or if they do not have opportunities for growth. However, if they have senior engineers as mentors, they may be more likely to stay with the company and grow within the team.

Senior engineers may also leave if they feel that they are not being challenged or if they do not have opportunities to mentor junior engineers. By having a mix of both senior and junior engineers, a team can provide opportunities for both groups to grow and develop within the team.

Costs and Sustainability

Finally, seniority balance can also impact costs and sustainability. Senior engineers may command higher salaries, which can impact the overall cost of the team. However, if they are mentoring junior engineers, those junior engineers can take on more responsibilities over time, potentially reducing the need for additional senior engineers in the future.

Additionally, if a team is composed solely of senior engineers, there may be a risk of knowledge loss if those engineers leave the company. By having a mix of both senior and junior engineers, the team can ensure that knowledge is transferred and retained within the team, providing long-term sustainability.

Scenarios and their impact

There is a shortage of published studies about seniority structures and their impact on organizations. Most articles tend to focus on only one dimension, like delivery capacity, mentorship capacity or structure sustainability. Here are some considerations for three different scenarios:

1. 50% seniors, 40% mids, and 10% juniors

This scenario has a high percentage of senior engineers, which can provide a wealth of experience and knowledge to the team. Senior engineers can mentor mid-level and junior engineers, providing opportunities for professional development and knowledge transfer. However, having a smaller percentage of junior engineers may not only limit the team’s ability to bring in fresh perspectives and new ideas (which can impact innovation and creativity) but also sustainability of the team, as senior engineers are often more prone to leave.

2. 20% seniors, 30% mids, and 50% juniors

This scenario has a higher percentage of junior engineers, which can provide opportunities for fresh perspectives and new ideas. However, having a lower percentage of senior engineers may limit the team’s ability to handle complex tasks and make significant decisions. Additionally, without enough mid-level engineers, there may not be enough people with the necessary experience to mentor junior engineers effectively.

3. 30% seniors, 40% mids, and 30% juniors

This scenario has a more balanced mix of senior, mid-level, and junior engineers. This balance can provide opportunities for mentoring, professional development, and knowledge transfer, while also allowing for fresh perspectives and new ideas. Having a mix of experience levels can also provide the team with the necessary expertise to handle complex tasks and make significant decisions.

Conclusion

In conclusion, seniority balance is critical for Software Engineering teams. It can impact delivery capacity, mentoring opportunities and capability, employee retention, costs, and sustainability. A team with a good mix of senior and junior engineers can provide a diverse range of skill sets, perspectives, and experience levels, which can lead to more innovative solutions and better overall performance. Therefore, it is important for companies to consider seniority balance when building and managing Software Engineering teams.


Seniority Balance in Software Engineering Teams was originally published in New Work Development on Medium, where people are continuing the conversation by highlighting and responding to this story.

]]>