How to send message from bot to Slack in Swift

Issue #560 Create a bot https://slack.com/intl/en-no/help/articles/115005265703-create-a-bot-for-your-workspace https://api.slack.com/bot-users#setup-events-api https://api.slack.com/bot-users#installing-bot Post message After adding bot to workspace, we’ll get OAuth Access Token and Bot User OAuth Access Token. Use Bot User OAuth Access Token to test drive bot message sending https://api.slack.com/methods/chat.postMessage/test The request url is like https://slack.com/api/chat.postMessage?token=xoxb-7212342835698-890815481123-abcdGgDEFfm2joQs1Vj5mABC&channel=random&text=hello&pretty=1 Code from Puma import Foundation public class Slack { public var name: String = "Send message to Slack" public var isEnabled = true private var message: Message?...

December 30, 2019 · 2 min · 346 words · Khoa

How to parse xcrun simctl devices

Issue #559 public class GetDestinations { public init() {} public func getAvailable(workflow: Workflow) throws -> [Destination] { let processHandler = DefaultProcessHandler(filter: { $0.starts(with: "name=") }) let string = try CommandLine().runBash( workflow: workflow, program: "xcrun simctl", arguments: [ "list", "devices", "-j" ], processHandler: processHandler ) guard let data = string.data(using: .utf8) else { throw PumaError.invalid } let response: Response = try JSONDecoder().decode(Response.self, from: data) let devicesWithOS: [DeviceWithOS] = response.devices.flatMap({ key, value in return value....

December 28, 2019 · 2 min · 249 words · Khoa

How to parse xcrun instruments devices

Issue #558 public class GetDestinations { public init() {} public func getAvailable(workflow: Workflow) throws -> [Destination] { let processHandler = DefaultProcessHandler(filter: { $0.starts(with: "name=") }) let string = try CommandLine().runBash( workflow: workflow, program: "xcrun instruments", arguments: [ "-s", "devices" ], processHandler: processHandler ) // Ex: iPad Air (11.0.1) [7A5EAD29-D870-49FB-9A9B-C81079620AC9] (Simulator) let destinations: [Destination] = try string .split(separator: "\n") .map({ String($0) }) .filter({ try $0.hasPattern(pattern: #"\[.+\]"#) }) .compactMap({ (line) -> Destination? in parse(line) }) return destinations } func parse(_ line: String) -> Destination?...

December 28, 2019 · 2 min · 241 words · Khoa

How to generate xml in Swift

Issue #556 Instead of learning XMLParser, we can make a lightweight version import Foundation public protocol XmlItem { func toLines() -> [String] } public struct XmlString: XmlItem { public let key: String public let value: String public init(key: String, value: String) { self.key = key self.value = value } public func toLines() -> [String] { return [ "<key>\(key)</key>", "<string>\(value)</string>" ] as [String] } } public struct XmlBool: XmlItem { public let key: String public let value: Bool public init(key: String, value: Bool) { self....

December 27, 2019 · 2 min · 259 words · Khoa

How to use KVO in Swift

Issue #554 A class must inherit from NSObject, and we have 3 ways to trigger property change Use setValue(value: AnyObject?, forKey key: String) from NSKeyValueCoding class MyObjectToObserve: NSObject { var myDate = NSDate() func updateDate() { setValue(NSDate(), forKey: "myDate") } } Use willChangeValueForKey and didChangeValueForKey from NSKeyValueObserving class MyObjectToObserve: NSObject { var myDate = NSDate() func updateDate() { willChangeValueForKey("myDate") myDate = NSDate() didChangeValueForKey("myDate") } } Use dynamic. See Swift Type Compatibility...

December 22, 2019 · 1 min · 182 words · Khoa

How to use precondition and assert in Swift

Issue #553 Read Swift asserts - the missing manual debug release release function -Onone -O -Ounchecked assert() YES NO NO assertionFailure() YES NO NO** precondition() YES YES NO preconditionFailure() YES YES YES** fatalError()* YES YES YES And from Interesting discussions on Swift Evolution – assert: checking your own code for internal errors – precondition: for checking that your clients have given you valid arguments. Read more https://stackoverflow.com/a/34379407/1418457

December 22, 2019 · 1 min · 67 words · Khoa

How to use regular expression in Swift

Issue #551 Find matches import Foundation public extension String { func matches(pattern: String) throws -> [String] { let regex = try NSRegularExpression(pattern: pattern) let results = regex.matches(in: self, options: [], range: NSRange(self.startIndex..., in: self)) return results.compactMap({ result in if let range = Range(result.range, in: self) { return String(self[range]) } else { return nil } }) } func hasPattern(pattern: String) throws -> Bool { return try !matches(pattern: pattern).isEmpty } } func testRegex() throws { let string = "iPad Air (11....

December 20, 2019 · 1 min · 152 words · Khoa

How to keep command line tool running with async in Swift

Issue #549 Use Semaphore public class Sequence: Task { public func run(workflow: Workflow, completion: @escaping TaskCompletion) { let semaphore = DispatchSemaphore(value: 0) runFirst(tasks: tasks, workflow: workflow, completion: { result in completion(result) semaphore.signal() }) semaphore.wait() } } public class Concurrent: Task { public func run(workflow: Workflow, completion: @escaping (Result<(), Error>) -> Void) { var runTaskCount = 0 let taskCount = tasks.count let semaphore = DispatchSemaphore(value: 0) tasks.forEach { task in task.run(workflow: workflow, completion: { _ in self....

December 18, 2019 · 1 min · 97 words · Khoa

How to sync an async function in Swift

Issue #547 func sync<T>(_ work: (@escaping ([T]) -> Void) -> Void) -> [T] { let semaphore = DispatchSemaphore(value: 1) var results = [T]() work { values in results = values semaphore.signal() } return results } sync({ completion in service.load(completion) })

December 18, 2019 · 1 min · 40 words · Khoa

How to use UICollectionViewLayout

Issue #546 Using the Flow Layout Customizing Collection View Cell Insertion Animations

December 17, 2019 · 1 min · 12 words · Khoa

How to not use protocol extension in Swift

Issue #542 With protocol extension See code Puma Build is UsesXcodeBuild is UsesCommandLine /// Any task that uses command line public protocol UsesCommandLine: AnyObject {} public extension UsesCommandLine { func runBash( workflow: Workflow, program: String, arguments: [String], processHandler: ProcessHandler = DefaultProcessHandler() ) throws { // Code } func runProcess( _ process: Process, workflow: Workflow, processHandler: ProcessHandler = DefaultProcessHandler() ) throws { // Code } } /// Any task that uses xcodebuild public protocol UsesXcodeBuild: UsesCommandLine { var xcodebuild: Xcodebuild { get set } } public extension UsesXcodeBuild { func runXcodeBuild(workflow: Workflow) throws { try runBash( workflow: workflow, program: "xcodebuild", arguments: xcodebuild....

December 15, 2019 · 1 min · 161 words · Khoa

How to show localized text in SwiftUI

Issue #533 struct ContentView: View { @Environment(\.locale) var locale: Locale var body: some View { VStack { Text(LocalizedStringKey("hello")) .font(.largeTitle) Text(flag(from: locale.regionCode!)) .font(.largeTitle) } } }

December 9, 2019 · 1 min · 25 words · Khoa

How to show flag emoji from country code in Swift

Issue #532 func flag(from country: String) -> String { let base : UInt32 = 127397 var s = "" for v in country.uppercased().unicodeScalars { s.unicodeScalars.append(UnicodeScalar(base + v.value)!) } return s } Read moree Swift turn a country code into a emoji flag via unicode https://github.com/onmyway133/Smile

December 9, 2019 · 1 min · 45 words · Khoa

How to do lense in Swift

Issue #528 What is lense https://www.schoolofhaskell.com/school/to-infinity-and-beyond/pick-of-the-week/a-little-lens-starter-tutorial A lens is a first-class reference to a subpart of some data type. For instance, we have _1 which is the lens that “focuses on” the first element of a pair. Given a lens there are essentially three things you might want to do View the subpart Modify the whole by changing the subpart Combine this lens with another lens to look even deeper...

December 4, 2019 · 1 min · 205 words · Khoa

How to convert from callback to Future Publisher in Combine

Issue #527 import Foundation import Combine public typealias TaskCompletion = (Result<(), Error>) -> Void public protocol Task: AnyObject { var name: String { get } func run(workflow: Workflow, completion: TaskCompletion) } public extension Task { func asPublisher(workflow: Workflow) -> AnyPublisher<(), Error> { return Future({ completion in self.run(workflow: workflow, completion: completion) }).eraseToAnyPublisher() } } let sequence = Publishers.Sequence<[AnyPublisher<(), Error>], Error>( sequence: tasks.map({ $0.asPublisher(workflow: self) }) )

December 2, 2019 · 1 min · 65 words · Khoa

How to make init with closure in Swift

Issue #526 public class Build: UsesXcodeBuild { public var arguments = [String]() public init(_ closure: (Build) -> Void = { _ in }) { closure(self) } } Use function builder public class Workflow { public var workingDirectory: String = "." public let tasks: [Task] public init(@TaskBuilder builder: () -> [Task]) { self.tasks = builder() self.tasks.forEach { task in task.workflow = self } } public init(@TaskBuilder builder: () -> Task) { self....

December 1, 2019 · 1 min · 113 words · Khoa

How to test a developing package with Swift Package Manager

Issue #525 Use macOS Command Line project Example Puma Create a new macOS project, select Command Line Tool Drag Puma.xcodeproj as a sub project of our test project Go to our TestPuma target, under Link Binary with Libraries, select Puma framework Puma has dependencies on PumaCore and PumaiOS, but in Xcode we only need to select Puma framework In code, we need to explicitly import PumaiOS framework if we use any of its classes...

November 30, 2019 · 2 min · 245 words · Khoa

How to use method from protocol extension in Swift

Issue #524 /// Any task that uses command line public protocol UsesCommandLine: AnyObject { var program: String { get } var arguments: Set<String> { get set } } public extension UsesCommandLine { func run() throws { let command = "\(program) \(arguments.joined(separator: " "))" Log.command(command) _ = try Process().run(command: command) } } class Build: UsesCommandLine { public func run() throws { arguments.insert("build") try (self as UsesCommandLine).run() } }

November 30, 2019 · 1 min · 67 words · Khoa

How to organize dependencies in Swift Package Manager

Issue #523 In Puma I want to make build tools for iOS and Android, which should share some common infrastructure. So we can organize dependencies like. Puma -> PumaAndroid, PumaiOS -> PumaCore -> xcbeautify, Files, Colorizer // swift-tools-version:5.1 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Puma", platforms: [.macOS("10.15")], products: [ .library(name: "Puma", targets: ["Puma"]) ], dependencies: [ ....

November 30, 2019 · 1 min · 145 words · Khoa

How to provide configurations in Swift

Issue #522 Sometimes ago I created Puma, which is a thin wrapper around Xcode commandline tools, for example xcodebuild There’s lots of arguments to pass in xcodebuild, and there are many tasks like build, test and archive that all uses this command. Use Options struct to encapsulate parameters To avoid passing many parameters into a class, I tend to make an Options struct to encapsulate all passing parameters. I also use composition, where Build....

November 30, 2019 · 3 min · 615 words · Khoa