Write Swift macros

RSS for tag

Discuss the WWDC23 Session Write Swift macros

View Session

Posts under wwdc2023-10166 tag

10 Posts
Sort by:
Post not yet marked as solved
4 Replies
2.3k Views
In Xcode 15.0.0 I have created a package using a template Swift Macro. I have named it 'MyMacroApple'. The template comes with #stringify macro so I have created an new app and named it 'MyMacroApp' then copy pasted the code from 'MyMacroApple' main.swift file import MyMacroApple let a = 17 let b = 25 let (result, code) = #stringify(a + b) then I have added Local package dependency to the app project and selected package product 'MyMacroApple' of Library kind to my 'pocMyApp' target. When I run the project I get the error: "No such module 'MyMacroApple'"
Posted Last updated
.
Post not yet marked as solved
3 Replies
1.8k Views
Hi. I created a new swift macro package and wan't to start using the template which contains the #stringify macro out of the box without any changes. The package itself runs incl. tests But now I added the Package to a dummy iOS app project / workspace and I can't compile if im using this code let (_, _) = #stringify(a + b) + package import Compile error: External macro implementation type '***.StringifyMacro' could not be found for macro 'stringify' I am using Xcode 15 and macOS 13.5.2 Restart Xcode, Clean etc doesn't help. Can anyone help my ? Thanks :)
Posted
by Nils.CEWE.
Last updated
.
Post not yet marked as solved
5 Replies
767 Views
I need to log to OSLog and into a file in parallel (due to OSLogStore not being able to provide old logs (FB13191608)). In Objective-C I can accomplish this with a macro using __FILE__ and __LINE__ in the macro implementation which still reference the position in the original code. I tried to create a Swift macro to make this work from Swift. However, log() takes the file and line number of the macro definition file instead of the position in the calling code. So when I click on the metadata link, I'm taken to the macro instead of the calling location. Is there any solution to that? #file and #line are correctly set in the macro but there’s no way to specify file and line number to the log() function (FB13204310).
Posted
by ortwin.
Last updated
.
Post not yet marked as solved
2 Replies
560 Views
I'm trying to create a macro that adds the file and line to a string that I use with OSLog. However, I only get warnings that either the String is not in the format of OSLogMessage or not an interpolated string. The Macro looks like this at the moment: public struct LocMacro: ExpressionMacro { public static func expansion( of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext ) -> ExprSyntax { guard let argument = node.argumentList.first?.expression.as(StringLiteralExprSyntax.self)?.segments else { fatalError("compiler bug: the macro does not have any StringLiteralExprSyntax arguments.") } guard let file = context.location(of: node)?.file.as(StringLiteralExprSyntax.self)?.segments, let line = context.location(of: node)?.line.as(IntegerLiteralExprSyntax.self) else { fatalError("compiler bug: the macro is unable to retrieve file and line numbers") } return "\"\(argument) - \(file):\(line)\"" } } and here the exposed macro: @freestanding(expression) public macro loc(_ text: String) -> String = #externalMacro(module: "LxoMacrosMacros", type: "LocMacro") I want to use it like this: import LxoMacros import OSLog let logger = Logger() let someNumber = 17 logger.debug(#loc("Working with some number \(someNumber)")) which should expand to: logger.debug("Working with some number \(someNumber) - MyFile.swift:8") Is there a way to change the macro so that it returns the correct type? When expending the macro it does show the right string, which works with print() and so on, but assuming since the Logger uses some sort of compiler check as well, it doesn't seem to work together as expected.
Posted Last updated
.
Post not yet marked as solved
1 Replies
330 Views
Hey guys, I have developed successfully a Macro that creates enum cased descriptions of type "String.LocalizationValue". Using the variable of these created ones and "String(localized: theVariable)" doesn't include them into my string catalog. So the whole point why I did failed at the last point. Can someone explain me if this will come or get fixed somewhere near in the future? thanks
Posted
by Bo0o0om.
Last updated
.
Post not yet marked as solved
0 Replies
362 Views
I wrote an @attached macro for generating an init. However, I also use this same class for previews. I get an error that it's impossible to create an instance of the class because the init that was supposed to be generated by the macro is missing. It seems that the #freestanding macro executes first, and at that moment, the code generated by @attached is not yet present. Is there a way to fix this?
Posted
by Horoko.
Last updated
.
Post not yet marked as solved
0 Replies
338 Views
I followed the WWDC session and Im not able to add breakpoint to the Macro for my unit tests.
Posted Last updated
.
Post not yet marked as solved
0 Replies
431 Views
Hi, I thought I'd ask as this sounds like the perfect use for Swift Macros and I'm betting other people ran into the same issue :) I'm currently trying to simplify [weak self] captures in some of my code. For example: // Usual syntax someAlert.addTextField { [weak self] textField in guard let self else { return } textField.text = somethingInSelf() } Adding a macro seems to trade one kind of wordiness for another: // Using a macro works, but is wordy because it requires non-trailing syntax someAlert.addTextField(configurationHandler: #weakSelf { textField in textField.text = somethingInSelf() }) Ideally I imagine there must be a way to do something like the code below: // Using the same macro fails when applied to a trailing closure someAlert.addTextField #weakSelf { textField in textField.text = somethingInSelf() } Does anyone have any suggestions? I feel this is probably a known/solved problem and I'm being a bit slow on the uptake :D
Posted Last updated
.
Post not yet marked as solved
0 Replies
516 Views
I'm trying to make a macro that has the following structure: @SampleDataProviding(decoder: JSONDecoder(), bundlePath: Bundle.module.bundlePath, fileExtension: "json") The primary issue that I am having is that I need to be able to scan a subdirectory of the bundlePath for files that match a certain criteria. However, when I try to access the Bundle.module.bundlePath it literally spits back at me "Bundle.module.bundlePath" which obviously doesn't get me anywhere. I've looked all over for a way to convert what I enter at the macro call site to an actual path that I can put in code that isn't generated. (This macro goes through a specific subdirectory looking for sample data files which it then generates a variable expression for each of them so that it is easy to access this sample data (for UI work or testing).) Each time I try this it fails stating that the folder doesn't exist because it's looking for "Bundle.module.bundlePath/[subdirectory]" when I want it to look for the subdirectory in the bundle path, not "Bundle.module.bundlePath". I've been able to get this to work in testing by wrapping the call in an interpolation from a string but this does not work in the real world because the compiler complains that the interpolation cannot be modified (it is also more clunky, I would prefer to avoid having to wrap the entire thing in an interpolation).
Posted
by thafner.
Last updated
.
Post marked as solved
2 Replies
636 Views
Hi, I've been playing around with macros and I found myself in a situation where I got attached accessor macro with 2 parameters - url and method. Both are strings. It looks like this public struct MyMacroMacro: AccessorMacro { public static func expansion<Context, Declaration>( of node: AttributeSyntax, providingAccessorsOf declaration: Declaration, in context: Context ) throws -> [AccessorDeclSyntax] where Context : MacroExpansionContext, Declaration : DeclSyntaxProtocol { guard let expressions = node.argument?.as(TupleExprElementListSyntax.self) else { //TODO throw error return [] } let segments = expressions.compactMap { $0.expression.as(StringLiteralExprSyntax.self)?.segments } let params = segments.compactMap { $0.trimmedDescription } guard params.count == 2 else { //TODO throw error return [] } return [ """ .init(url: url + "\(raw: params[0])", method: "\(raw: params[1])") """ ] } } Now, when I call it like: struct MyStruct { let url = "https://apple.com" @MyMacro(url: "/test", method: "some") var myVar: MyCustomType it produces something like this: struct MyStruct { let url = "https://apple.com" var myVar: MyCustomType { .init(url: url + " /test", method: " some") } I have no idea why it adds those whitespaces. When debugging, before calling "return" there are no whitespaces in "/test" or "some". Also - is there a way to create a macro that'll call another macro? For example MySecondMacro(url: "/url") to call MyMacro(url: "/url", method: "second")?
Posted
by neoth.
Last updated
.