I am curious how I might set up the following:
Create a core Swift Package with some basic functionality.
Have other optional packages that dovetail with the core package (directly related). Could be 10+ function blocks of functionality.
So in a swift package, you hard-code in dependencies. I don't want that. To keep app size to a minimum and allowing developers to pick function blocks of code as they need, would one have a developer include the core package, and then other packages with specific functionality as they need - which somehow rely on the core package?
Would you just add the core swift package and then each function block swift package?
I am wondering how this could be set up to allow for flexibility for developers - keeping core function blocks separate so they would be pulled into a project as needed. Keeping things as simple as possible.
In a layered architecture there is typically no need for optional dependencies. In more messy dependency graphs, it can be really useful to have optional dependencies.
How to implement an optional dependency in a Swift Package?
A: Just don't add the dependency in the Package.swift file.
To use the Core package (lets call it MyCorePackage) the code will need to check if that package can be imported, like so:
#if canImport(MyCorePackage)
import MyCorePackage
#endif
struct MyRelatedType { ... }
#if canImport(MyCorePackage)
extension MyRelatedType {
var asCoreType: CoreType { ... }
}
#endif
Related
If not - are there are any plans to make it so?
I know that dart itself has different backends that might have different implementation. I was curious if users can do the same, e.g. have a function that delegates to one #patch for js runtime and to another #patch in case of dart vm.
If you're creating a package, you can export different implementations for different platforms, see here: https://dart.dev/guides/libraries/create-library-packages#conditionally-importing-and-exporting-library-files
The way to make platform specific code (or rather platform library specific code) is to use conditional imports.
The syntax is:
import "helper_default.dart"
if (dart.library.js) "helper_js.dart"
if (dart.library.io) "helper_io.dart";
This import statement will import one of three libraries. If the dart:js library is available on the platform you are compiling for, you will import helper_js.dart. If not, and dart:io is available, then you will import helper_io.dart. If that's also not there, you will import helper_default.dart.
The trick is then to have the same library API implemented in all three libraries, but based only on the available platform library.
The helper_js.dart library can implement something using dart:js and helper_io.dart can import dart:io. The default library will likely not be able to do anything useful, so it might just throw an UnsupportedError if you try to use it.
The code in the importing library must be valid for all three imports, which is why they must have "the same" API. That might include declaring a class, but you'd usually define the interface of that class in a non-platform dependent library that the three implementations can share, to ensure that they actually have the same interface.
Example:
library mypkg.shared_types;
import "helper_default.dart"
if (dart.library.js) "helper_js.dart"
if (dart.library.io) "helper_io.dart"
as helper;
class SharedClass {
factory SharedClass(int something) = helper.SharedClassImpl;
int get value;
}
and
library mypkg.helper_io;
import "dart:io";
import "shared_types.dart";
class SharedClassImpl implements SharedClass {
int _id;
SharedClassImpl(this._id);
int get value { // Something depending on dart:io.
var bytes = File("pathToSomething-$id").readAsBytesSync();
return bytes[0];
}
}
I've been trying to locate a transition guide for Swift 2, in particular things developers should be aware of when migrating Swift 1/1.2 codebases over to Swift 2. Obviously you have the migration assistant in Xcode, but that only really covers the donkey work and not the stuff that requires a bit more intelligent thought.
Based on the resources I was able to find on Swift 2, I've put together the following checklist:
try/catch/throw error handling - to be used for recoverable errors; revise error handling code accordingly. In particular, check all uses of NSError and calling back to delegates to report recoverable errors.
Use enums conforming to ErrorType to define your own meaningful errors.
Use #available for accessing newer platform APIs - check API use against app Deployment Target and revise accordingly
protocol extensions - move as much code as possible into these to aid re-use. In particular refactor Global Functions into protocol extensions.
nullability annotations & generics - remove redundant optional bindings and type castings
Use do { } to control scope and free large resources early
Move old do { ... } while loops to repeat { } (to remove ambiguity and improve readability)
Use guard to return early and avoid excessive indentation
Use defer for cleanup code like closing files etc.
Use Option Sets rather than OR-ing values together (e.g. viewAnimationOptions = [.Repeat, .CurveEaseIn, .TransitionCurlUp])
Review public accessor specifiers which were previously only required to support testing. Use #testable and import MyApp instead.
Move single-case switch statements to the new if case .MyEnumCase(let value) = bar() where value != 42 { doThing(value) }
Use "for ... in" filtering to clean up for loops containing if filtering statements e.g. for value in mySequence where value != "" { }
native support for C function pointers - provide using closures or global functions (do not capture local context when doing so)
fix any new let/var warnings
fix any unused variable warnings
Failable initializers can now return nil before calling super.init - remove any previous workarounds required. Designated initializers still have to initialize all stored properties before returning nil however.
Sources:
https://developer.apple.com/swift/blog/?id=29
https://developer.apple.com/swift/
https://developer.apple.com/library/prerelease/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc7_release_notes.html#//apple_ref/doc/uid/TP40001051-CH5-SW1
https://developer.apple.com/videos/wwdc/2015/?id=106
http://www.raywenderlich.com/108522/whats-new-in-swift-2
What have I missed?
Part of the problem is that Swift 2 has continued to evolve past WWDC. So even this year's WWDC videos are already potentially out of date, or at least not the whole story.
Unfortunately, at this time there is no official "transition guide" from Apple as such.
The Swift Programming Language (Swift 2) is always updated by Apple whenever they release a new version of Swift and is therefore one of the best sources for up to date information about Swift 2 (or later). There is plenty of explanation and example code of the entire language, not just the changes, but this is definitely at least on of the best sources for the information you are looking for right now.
Using swift I created a framework Common that contains functions and protocols I use repeatedly to cut down on code reuse.
Common.framework
public protocol CommonProtocol {}
I than created a framework that I want to share with others which includes some classes that extends CommonProtocol and passes CommonProtocol in response to some function calls.
Sharable.framework
public class Sharable : CommonProtocol {
func getCommon() -> CommonProtocol
}
Unfortunately when I attempt to use Sharable.framework in a Project I get there error:
Swift Compiler ErrorUse of undeclared type 'CommonProtocol'
Does anyone know how to make the protocol visible to Modules that use the Sharable.framework?
I am copying the frame Common.framework in the Copy Files step to Destination Frameworks (there was no noticeable change when I made it Shared Frameworks)
If possible I would prefer to only make certain protocols from Common.framework visible through Sharable.framework and I don't want to force my users to import multiple frameworks if I can avoid it.
It's possible to add extensions to existing Swift object types using extensions, as described in the language specification.
As a result, it's possible to create extensions such as:
extension String {
var utf8data:NSData {
return self.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
}
}
However, what's the best naming practice for Swift source files containing such extensions?
In the past, the convention was to use extendedtype+categoryname.m for the Objective-C
type as discussed in the Objective-C guide. But the Swift example doesn't have a category name, and calling it String.swift doesn't seem appropriate.
So the question is: given the above String extension, what should the swift source file be called?
Most examples I have seen mimic the Objective-C approach. The example extension above would be:
String+UTF8Data.swift
The advantages are that the naming convention makes it easy to understand that it is an extension, and which Class is being extended.
The problem with using Extensions.swift or even StringExtensions.swift is that it's not possible to infer the purpose of the file by its name without looking at its contents.
Using xxxable.swift approach as used by Java works okay for protocols or extensions that only define methods. But again, the example above defines an attribute so that UTF8Dataable.swift doesn't make much grammatical sense.
I prefer having a + to underline the fact it contains extensions :
String+Extensions.swift
And if the file gets too big, you can then split it for each purpose :
String+UTF8Data.swift
String+Encrypt.swift
There is no Swift convention. Keep it simple:
StringExtensions.swift
I create one file for each class I'm extending. If you use a single file for all extensions, it will quickly become a jungle.
I prefer StringExtensions.swift until I added too much things to split the file into something like String+utf8Data.swift and String+Encrypt.swift.
One more thing, to combine similar files into one will make your building more faster. Refer to Optimizing-Swift-Build-Times
Rather than adding my comments all over the place, I'm surfacing them all here in one answer.
Personally, I take a hybrid approach that gives both good usability and clarity, while also not cluttering up the API surface area for the object that I'm extending.
For instance, anything that makes sense to be available to any string would go in StringExtensions.swift such as trimRight() and removeBlankLines().
However, if I had an extension function such as formatAsAccountNumber() it would not go in that file because 'Account Number' is not something that would naturally apply to any/all strings and only makes sense in the context of accounts. In that case, I would create a file called Strings+AccountFormatting.swift or maybe even Strings+CustomFormatting.swift with a formatAsAccountNumber() function if there are several types/ways to actually format it.
Actually, in that last example, I actively dissuade my team from using extensions like that in the first place, and would instead encourage something like AccountNumberFormatter.format(String) instead as that doesn't touch the String API surface area at all, as it shouldn't. The exception would be if you defined that extension in the same file where it's used, but then it wouldn't have it's own filename anyway.
If you have a team-agreed set of common and miscellaneous enhancements, lumping them together as an Extensions.swift works as Keep-It-Simple first level solution. However, as your complexity grows, or the extensions become more involved, a hierarchy is needed to encapsulate the complexity. In such circumstances I recommend the following practice with an example.
I had a class which talks to my back-end, called Server. It started to grow bigger to cover two different target apps. Some people like a large file but just logically split up with extensions. My preference is to keep each file relatively short so I chose the following solution. Server originally conformed to CloudAdapterProtocol and implemented all its methods. What I did was to turn the protocol into a hierarchy, by making it refer to subordinate protocols:
protocol CloudAdapterProtocol: ReggyCloudProtocol, ProReggyCloudProtocol {
var server: CloudServer {
get set
}
func getServerApiVersion(handler: #escaping (String?, Error?) -> Swift.Void)
}
In Server.swift I have
import Foundation
import UIKit
import Alamofire
import AlamofireImage
class Server: CloudAdapterProtocol {
.
.
func getServerApiVersion(handler: #escaping (String?, Error?) -> Swift.Void) {
.
.
}
Server.swift then just implements the core server API for setting the server and getting the API version. The real work is split into two files:
Server_ReggyCloudProtocol.swift
Server_ProReggyCloudProtocol.swift
These implement the respective protocols.
It means you need to have import declarations in the other files (for Alamofire in this example) but its a clean solution in terms of segregating interfaces in my view.
I think this approach works equally well with externally specified classes as well as your own.
Why is this even a debate? Should I put all my sub classes into a file called _Subclasses.swift. I think not. Swift has module based name spacing. To extend a well known Swift class needs a file that is specific to its purpose. I could have a large team that creates a file that is UIViewExtensions.swift that express no purpose and will confuse developers and could be easily duplicated in the project which would not build. The Objective-C naming convention works fine and until Swift has real name spacing, it is the best way to go.
In Swift it's not necessary to prefix classes anymore as their module acts as the namespace.
What about prefixing extension functions? For example:
extension UIImage {
public func hnk_hasAlpha() -> Bool { ... }
}
On one hand Swift is not dynamic so collisions would generate compiler errors.
But what happens if compiled code runs in a future iOS/OS X version in which one of my extension methods is added? Would methods in different modules be considered different symbols even if they have the same signature?
Does it make a difference if the extended class is a NSObject subclass or a pure Swift class?
There's some subtlety here:
Extensions of Objective-C types are implemented as Objective-C categories, with all that implies.
Extensions of Swift types, however, are only in effect where visible due to imports. This means that you can't accidentally stomp on a private system method (whether now or one introduced in the future), and if the system introduces a public method with the same name as yours, you'll get a compile-time failure when you rebuild, but your existing app won't break.
You should check those threads also:
Name collisions for extension methods from different frameworks - quote from thread:
regardless the application code imports which Framework, it seems, the actual called implementation depends on the order in Linked Frameworks and Libraries in "First come, first served" manner. But, as far as I know, this behavior is not guaranteed.
and Swift Extension: same extension function in two Modules which also confirms the same problem.
So based on that for Objective-C objects such as UIImage from your example name collisions are possible and you might see unexpected behaviour if there will be two methods with the same name in two different extensions for Objective-C object.