技术控

    今日:59| 主题:49369
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] Writing Libraries for Swift 2.x and 3.0 Compatibility

[复制链接]
来自星星的叫兽 发表于 2016-10-4 16:56:58
40 3

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
This is a challenging time to be a Swift developer. With three versions of Swift in the wild (2.2, 2.3, and 3.0) and two active SDKs with source-breaking changes, maintaining libraries is a daunting task. The two most common approaches to supporting multiple version of Swift and the SDKs are creating a Git branch per version, or supporting both 2.x and 3.0    using              #if          to isolate compatibility issues.  
  When I started development on a    Swift-first version of BonMot, I didn’t like either of these options. However, given my release window being in the middle of the Swift 2.3 to 3.0 transition, I felt as though I should give it a shot. I was encouraged when I heard that    OHHTTPStubswas able to support both 2.x and 3.0, and decided to see if I could use the          #if    approach in a way that minimizes the impact to the code base. This post summarizes a few strategies I found that have made maintaining both 2.x and 3.0 reasonably pain-free.  
  Call Site Compatibility

  Call site compatibility is when a method has the same method signature in Swift 2.x and 3.0. There are two aspects of Swift 3.0 that affect this that were part of    “The Great Renaming”. The first is dropping the implicit          _    argument label applied to the first argument, which makes the labeling behavior of all arguments consistent. The second part is moving part of the method name into the first argument label.  
  For example, given a method that takes a          String    as an argument:  
  1. func performThing(thing: String) {}
  2.  
  3. // Verbose function signatures and the call-site invocation
  4. #if swift(>=3.0)
  5.     func performThing(thingthing: String) {}
  6.     performThing(thing: "hello")
  7. #else
  8.     func performThing(_ thing: String) {}
  9.     performThing("hello")
  10. #endif
复制代码
To write code that is call-site compatible, always provide a label for your first argument. This avoids the implicit          _    argument label that is inserted by Swift 2.x. By avoiding this behavior, you ensure that the signature is the same in Swift 2.x and 3.0.  
  For example:
  1. // Don’t do this. Calling semantics change between 2.x and 3.0
  2. func performThing(thing: String) {}
  3.  
  4. // Avoid this even though it is call-site-compatible. This will generate a warning
  5. // in Swift 2.x because the '_' label is redundant.
  6. func performThing(_ thing: String) {}
  7.  
  8. // Avoid this even though it is call-site-compatible. This will generate a warning
  9. // in Swift 3.0 because the 'thing' label is redundant.
  10. func performThing(thingthing: String) {}
  11.  
  12. // Just right! The argument label and parameter name are different,
  13. // which avoids the Swift 3.0 warning mentioned above.
  14. func perform(thingtheThing: String) {}
复制代码
If you really want no argument label, you will need a          #if    statement and a duplication of the method. This may be worth it for your API, but if you choose this option, I recommend that your Swift 2.x and 3.0 versions call out to a shared private function to minimize the duplication.  
  By writing all of your method signatures to be call site compatible, you provide your API consumers with a source-stable API between Swift 2.x and 3.0, and you can invoke your own methods inside your code without littering          #if    statements throughout your code.  
  Compatibility.swift

  Now that we can maintain call site compatibility in our source code, we need a strategy for using the Swift Standard Library and Cocoa libraries in a call site-compatible manner. I’ve created a Compatibility.swift file that contains Swift 2.x extensions on every object that was part of The Great Renaming. This is a bit more painful and requires substantially more grunt work than I like; however, it isolates the impact of The Great Renaming to a single file.
  For example:
  1. #if swift(>=3.0)
  2. #else
  3.     typealias OptionSet = OptionSetType
  4.     extension UIApplication {
  5. [email protected] var shared:UIApplication {
  6.             return sharedApplication()
  7.         }
  8.     }
  9.     extension NSAttributedString {
  10. [email protected] attributes(atlocation: Int, effectiveRangerange: NSRangePointer) -> [String : AnyObject] {
  11.             return attributesAtIndex(location, effectiveRange: range)
  12.         }
  13.     }
  14.     // and so on
  15. #endif
复制代码
Note the typealias of          OptionSet    to          OptionSetType    . This allows you to write Swift 2.3 code in a way that will more easily migrate to Swift 3.0 when the time is right.  
  There are a few cases where this strategy falls a bit short. In particular, some Swift 3.0 API’s are not valid Swift 2.3. For instance,          NSParagraphStyle.default    is not valid Swift 2.3, so I was forced to use a prefixed version.  
  Do Not Pollute!

  All of the functions in your Compatibility.swift file should not be          public    , because they should not pollute your consumer app’s namespace. Also, they must be marked with          @nonobjc final    or          @nonobjc static    if they are on          NSObject    subclasses. This will prevent the Swift compiler from creating Objective-C selectors. Instead, it will use static dispatch, which the compiler will probably end up inlining.  
  Type Changes

  Swift 3.0 also imports    String consts as Enumerations, a welcome change. However, this causes a new complication that has two compatibility challenges. The first is the type declaration changes. This is easily remedied through the use of          typealias    . In BonMot, I was able to          typealias              BonMotContentSizeCategory    to          String    in iOS 9, and          UIContentSizeCategory    in iOS 10. This can get trickier, though. For instance, if I had to store this value in an attribute dictionary, I’d have to add          #if    or some more compatibility code. In the BonMot unit tests, I    didend up having to call methods that used          BonMotContentSizeCategory    , so I added my own          UIContentSizeCategory    struct to the test target under Swift 2.3 to make the unit tests compatible. This is not something that you would want to ship with the library, but it did help making sure the tests run in both 2.x and 3.0.  
  Managing SDK differences

  SDK differences between iOS 9 and 10 (substitute macOS, watchOS, or tvOS as appropriate) can result in compilation failures. Swift 2.3 and 3.0 don’t provide a clean mechanism for dealing with this, since the only applicable check is the Swift version check. However, you can use the Swift version as a proxy for the SDK version.
  For example:
  1. #if swift(>=2.2)
  2.     // This can only be compiled for iOS 9
  3. #elseif swift(>=2.3)
  4.     // This can only be compiled for iOS 10
  5. #elseif swift(>=3.0)
  6.     // This can only be compiled for iOS 10
  7. #endif
复制代码
Hopefully, the next version of Swift will provide SDK checks to the          #if    system, as the intent here is masked by the use of Swift language version checks. However, this is the current preferred method of dealing with SDK differences. For instance, in iOS 10, a lot of the          (NS)URL    methods changed from returning          (NS)URL    to returning          (NS)URL?    .  
  Delegate Methods & Subclasses

  One place where The Great Renaming can force          #if    is with delegates and subclasses. When implementing a delegate or subclass whose method signature has been impacted by The Great Renaming, you have to use          #if    to avoid warnings. If you are using a lot of delegates, feel free to make compilation under 3.0 be nice and clear, and leave some warnings for those stuck with 2.x. Hopefully, the transition period won’t be long enough to lose sleep over it.  
  Compatible Swift Subset

  Another trick to minimize the amount of          #if    is to use a subset of Swift that is compatible with both 2.x and 3.0. Below is a non-exhaustive list of features that you should avoid, or if you need them, must be guarded in          #if    and disappointment.  
  where

  Swift 3.0 deprecated the          if let ... where        pattern. These must be broken up into multiple statements. Also,          where    must be removed from generic statements.  
  Argument annotations

  Swift 3.0 shifted the    argument    annotations(such as          inout    ) so that the annotation is applied to the type instead of the label. Unfortunately, there is no way to provide compatibility without          #if    here.  
  Access Control

  Swift 3.0    changedthe meaning of the          private    keyword from “private to the file” to “private to the enclosing declaration,” and added a          fileprivate    keyword. This means that, in code that requires compilation in Swift 2.x and 3.0, you can use only          private    to mean “private to the enclosing declaration.” In the context of maintaining a library, leaving things as          internal    will typically be sufficient, and neatly sidesteps the issue of worrying about          private    versus          fileprivate    .  
  Design for the Future

  When a convention changes between Swift 2.x and 3.0, use the 3.0 convention. Enumerations, for instance, should have a lowercase initial letter. It breaks the Swift 2.x convention, but it’s only for the hopefully brief transition period.
  What Else?

  The above samples are not an exhaustive set of all of the Swift 2.x and 3.0 compatibility issues. Attempting to write a compatible branch for your library may cause more problems than it’s worth. With these approaches, I was able to isolate the majority of the compatibility code to one file, and in my case this was a very clear win. Hopefully, Swift 2.3 is a quick blip in the evolution of Swift, and we can drop support quickly. However, Swift 2.3 is fully supported for iOS 10.0, and there will be many disappointed Swift 2.3 users if you don’t support them somehow.
友荐云推荐




上一篇:Using NLP + Neo4j for a Social Media Recommendation Engine
下一篇:Beware! You Can Get Hacked Just by Opening a 'JPEG 2000' Image
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

刺猬要怎么拥抱ベ 发表于 2016-10-4 18:17:38
在乎的人不明白,明白的人不在乎。
回复 支持 反对

使用道具 举报

春绿 发表于 2016-10-4 18:48:59
很经典,收藏了!
回复 支持 反对

使用道具 举报

1988128 发表于 2016-10-11 19:22:53
生命中的第一个沙发,激动嘞!
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表