Skip to content

Commit

Permalink
Merge pull request #17 from eddiekaiger/swift-4
Browse files Browse the repository at this point in the history
Swift 4 and 3.2
  • Loading branch information
eddiekaiger authored Sep 16, 2017
2 parents 28ad388 + 8b27637 commit 33939dd
Show file tree
Hide file tree
Showing 22 changed files with 732 additions and 248 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
language: objective-c
osx_image: xcode8
osx_image: xcode9

env:
- PLATFORM=iOS CODECOV=ios DESTINATION='platform=iOS Simulator,name=iPhone 6S'
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
source 'https://rubygems.org'

gem 'cocoapods', '~> 1.1.1'
gem 'cocoapods', '~> 1.3.0'
59 changes: 31 additions & 28 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,69 +1,72 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (4.2.7.1)
CFPropertyList (2.3.5)
activesupport (4.2.9)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
claide (1.0.1)
cocoapods (1.1.1)
claide (1.0.2)
cocoapods (1.3.1)
activesupport (>= 4.0.2, < 5)
claide (>= 1.0.1, < 2.0)
cocoapods-core (= 1.1.1)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.3.1)
cocoapods-deintegrate (>= 1.0.1, < 2.0)
cocoapods-downloader (>= 1.1.2, < 2.0)
cocoapods-downloader (>= 1.1.3, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-stats (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.1.1, < 2.0)
cocoapods-trunk (>= 1.2.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored (~> 1.2)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (~> 2.0.1)
gh_inspector (~> 1.0)
molinillo (~> 0.5.1)
molinillo (~> 0.5.7)
nap (~> 1.0)
xcodeproj (>= 1.3.3, < 2.0)
cocoapods-core (1.1.1)
activesupport (>= 4.0.2, < 5)
ruby-macho (~> 1.1)
xcodeproj (>= 1.5.1, < 2.0)
cocoapods-core (1.3.1)
activesupport (>= 4.0.2, < 6)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
cocoapods-deintegrate (1.0.1)
cocoapods-downloader (1.1.2)
cocoapods-downloader (1.1.3)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.0)
cocoapods-stats (1.0.0)
cocoapods-trunk (1.1.1)
cocoapods-trunk (1.2.0)
nap (>= 0.8, < 2.0)
netrc (= 0.7.8)
cocoapods-try (1.1.0)
colored (1.2)
colored2 (3.1.2)
escape (0.0.4)
fourflusher (2.0.1)
fuzzy_match (2.0.4)
gh_inspector (1.0.2)
i18n (0.7.0)
json (1.8.3)
minitest (5.9.1)
molinillo (0.5.2)
gh_inspector (1.0.3)
i18n (0.8.6)
minitest (5.10.3)
molinillo (0.5.7)
nanaimo (0.2.3)
nap (1.1.0)
netrc (0.7.8)
thread_safe (0.3.5)
tzinfo (1.2.2)
ruby-macho (1.1.0)
thread_safe (0.3.6)
tzinfo (1.2.3)
thread_safe (~> 0.1)
xcodeproj (1.3.3)
activesupport (>= 3)
claide (>= 1.0.1, < 2.0)
colored (~> 1.2)
xcodeproj (1.5.1)
CFPropertyList (~> 2.3.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.2.3)

PLATFORMS
ruby

DEPENDENCIES
cocoapods (~> 1.1.1)
cocoapods (~> 1.3.0)

BUNDLED WITH
1.13.6
25 changes: 8 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

#### *A Swifty API for attributed strings.*

![Swift Version](https://img.shields.io/badge/swift-3.0-orange.svg?style=flat)
![Swift Version](https://img.shields.io/badge/swift-4-orange.svg?style=flat)
![Swift Version](https://img.shields.io/badge/swift-3.2-orange.svg?style=flat)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/SwiftyAttributes.svg)](https://img.shields.io/cocoapods/v/SwiftyAttributes.svg)
[![Platform](https://img.shields.io/cocoapods/p/SwiftyAttributes.svg?style=flat)](http://cocoapods.org/pods/SwiftyAttributes)
Expand Down Expand Up @@ -38,29 +39,19 @@ let fancyString = "Hello".withFont(.systemFont(ofSize: 12)) + " World!".withFont
# Requirements

* iOS 8.0+, macOS 10.11+, watchOS 2.0+, tvOS 9.0+
* Swift 4.0 or 3.2
* Xcode 9

# Installation

### With CocoaPods

#### For **Swift 3**:

`pod 'SwiftyAttributes'`

#### For **Swift 4 Beta**:

`pod 'SwiftyAttributes', :git => 'https://github.com/eddiekaiger/SwiftyAttributes.git', :branch => 'swift-4'`

### With Carthage

#### For **Swift 3**:

`github "eddiekaiger/SwiftyAttributes"`

#### For **Swift 4 Beta**:

`github "eddiekaiger/SwiftyAttributes" "swift-4"`

# Usage

Initializing attributed strings in `SwiftyAttributes` can be done several ways:
Expand All @@ -82,7 +73,7 @@ Initializing attributed strings in `SwiftyAttributes` can be done several ways:

You can retrieve the attribute at a specific location using an attribute name from the `Attribute.Name` enum:
````swift
let attr: Attribute? = myAttributedString.attribute(.shadow, at: 5)
let attr: Attribute? = myAttributedString.swiftyAttribute(.shadow, at: 5)
````

Several API methods are provided to use these new enums as well as Swift's `Range` type instead of `NSRange`. Some of the method signatures include:
Expand All @@ -96,18 +87,18 @@ extension NSMutableAttributedString {
func replaceCharacters(in range: Range<Int>, with str: String)
func replaceCharacters(in range: Range<Int>, with attrString: NSAttributedString)
func deleteCharacters(in range: Range<Int>)
func removeAttribute(_ name: Attribute.Name, range: Range<Int>)
func removeAttribute(_ name: NSAttributedStringKey, range: Range<Int>)
}

extension NSAttributedString {
convenience init(string str: String, attributes: [Attribute])
func withAttributes(_ attributes: [Attribute]) -> NSMutableAttributedString
func withAttribute(_ attribute: Attribute) -> NSMutableAttributedString
func attributedSubstring(from range: Range<Int>) -> NSAttributedString
func attribute(_ attrName: Attribute.Name, at location: Int, effectiveRange range: NSRangePointer? = nil) -> Attribute?
func swiftyAttribute(_ attrName: NSAttributedStringKey, at location: Int, effectiveRange range: NSRangePointer? = nil) -> Attribute?
func attributes(in range: Range<Int>, options: NSAttributedString.EnumerationOptions = []) -> [([Attribute], Range<Int>)]
func enumerateAttributes(in enumerationRange: Range<Int>, options: NSAttributedString.EnumerationOptions = [], using block: (_ attrs: [Attribute], _ range: Range<Int>, _ stop: UnsafeMutablePointer<ObjCBool>) -> Void)
func enumerateAttribute(_ attrName: Attribute.Name, in enumerationRange: Range<Int>, options: NSAttributedString.EnumerationOptions = [], using block: (_ value: Any?, _ range: Range<Int>, _ stop: UnsafeMutablePointer<ObjCBool>) -> Void)
func enumerateAttribute(_ attrName: NSAttributedStringKey, in enumerationRange: Range<Int>, options: NSAttributedString.EnumerationOptions = [], using block: (_ value: Any?, _ range: Range<Int>, _ stop: UnsafeMutablePointer<ObjCBool>) -> Void)
}

extension String {
Expand Down
4 changes: 2 additions & 2 deletions SwiftyAttributes.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Pod::Spec.new do |s|

s.name = "SwiftyAttributes"
s.version = "3.2.0"
s.version = "4.0.0"
s.summary = "A Swifty API for attributed strings."

s.description = <<-DESC
Expand All @@ -14,7 +14,7 @@ Pod::Spec.new do |s|
s.homepage = "https://github.com/eddiekaiger/SwiftyAttributes"
s.license = { :type => "MIT", :file => "LICENSE" }
s.author = { "Eddie Kaiger" => "eddiekaiger@gmail.com" }
s.source = { :git => "https://github.com/eddiekaiger/SwiftyAttributes.git", :tag => "v3.2.0" }
s.source = { :git => "https://github.com/eddiekaiger/SwiftyAttributes.git", :tag => "v4.0.0" }

s.source_files = "SwiftyAttributes/Sources/common/*.swift"
s.osx.source_files = "SwiftyAttributes/Sources/macOS/*.swift"
Expand Down
6 changes: 2 additions & 4 deletions SwiftyAttributes.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,6 @@
PRODUCT_BUNDLE_PACKAGE_TYPE = FMWK;
PRODUCT_NAME = SwiftyAttributes;
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
};
name = Debug;
};
Expand All @@ -652,7 +651,6 @@
PRODUCT_BUNDLE_PACKAGE_TYPE = FMWK;
PRODUCT_NAME = SwiftyAttributes;
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
};
name = Release;
};
Expand All @@ -665,7 +663,6 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.eddiekaiger.SwiftyAttributesTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
Expand All @@ -678,7 +675,6 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.eddiekaiger.SwiftyAttributesTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
};
name = Release;
};
Expand Down Expand Up @@ -731,6 +727,7 @@
SUPPORTED_PLATFORMS = "watchsimulator watchos appletvos appletvsimulator iphoneos iphonesimulator macosx";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2,3,4";
TVOS_DEPLOYMENT_TARGET = 9.0;
VERSIONING_SYSTEM = "apple-generic";
Expand Down Expand Up @@ -780,6 +777,7 @@
PRODUCT_BUNDLE_PACKAGE_TYPE = BNDL;
SUPPORTED_PLATFORMS = "watchsimulator watchos appletvos appletvsimulator iphoneos iphonesimulator macosx";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2,3,4";
TVOS_DEPLOYMENT_TARGET = 9.0;
VALIDATE_PRODUCT = YES;
Expand Down
37 changes: 24 additions & 13 deletions SwiftyAttributes/Sources/common/Attribute+Sequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,37 @@
A Sequence with an iterator of (String, Any) is equivalent to [String: Any]
This is a simple syntactic workaround since we can't write "extension Dictionary where Key == String". Thanks Swift :)
*/
extension Sequence where Iterator.Element == (key: String, value: Any) {

/// Returns an array of `Attribute`s converted from the dictionary of attributes. Use this whenever you want to convert [String: Any] to [Attribute].
public var swiftyAttributes: [Attribute] {
return flatMap { name, value in
if let attrName = Attribute.Name(rawValue: name) {
return Attribute(name: attrName, foundationValue: value)
} else {
return nil
}
#if swift(>=4.0)
extension Dictionary where Key == NSAttributedStringKey {

/// Returns an array of `Attribute`s converted from the dictionary of attributes. Use this whenever you want to convert [NSAttributeStringKey: Any] to [Attribute].
public var swiftyAttributes: [Attribute] {
return map(Attribute.init)
}

}
#else
extension Sequence where Iterator.Element == (key: String, value: Any) {

}
/// Returns an array of `Attribute`s converted from the dictionary of attributes. Use this whenever you want to convert [String: Any] to [Attribute].
public var swiftyAttributes: [Attribute] {
return flatMap { name, value in
if let attrName = AttributeName(rawValue: name) {
return Attribute(name: attrName, foundationValue: value)
} else {
return nil
}
}
}

}
#endif

extension Sequence where Iterator.Element == Attribute {

/// Returns the attribute dictionary required by Foundation's API for attributed strings. Use this whenever you need to convert [Attribute] to [String: Any].
public var foundationAttributes: [String: Any] {
return reduce([String: Any]()) { dictionary, attribute in
public var foundationAttributes: [StringKey: Any] {
return reduce([StringKey: Any]()) { dictionary, attribute in
var dict = dictionary
dict[attribute.keyName] = attribute.foundationValue
return dict
Expand Down
22 changes: 16 additions & 6 deletions SwiftyAttributes/Sources/common/Attribute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ public typealias Shadow = NSShadow
public typealias TextAttachment = NSTextAttachment
#endif

#if swift(>=4.0)
public typealias AttributeName = NSAttributedStringKey
#else
public typealias AttributeName = Attribute.Name
#endif

/**
Represents attributes that can be applied to NSAttributedStrings.
*/
Expand Down Expand Up @@ -130,7 +136,7 @@ public enum Attribute {
/// The writing directions to apply to the attributed string. See `WritingDirection` for values. Only available on iOS 9.0+.
case writingDirections([WritingDirection])

init(name: Attribute.Name, foundationValue: Any) {
init(name: AttributeName, foundationValue: Any) {
func validate<Type>(_ val: Any) -> Type {
assert(val is Type, "Attribute with name \(name.rawValue) must have a value of type \(Type.self)")
return val as! Type
Expand Down Expand Up @@ -180,7 +186,7 @@ public enum Attribute {
case .strokeWidth: ret = .strokeWidth(validateDouble(foundationValue))
case .strikethroughColor: ret = .strikethroughColor(validate(foundationValue))
case .strikethroughStyle: ret = .strikethroughStyle(StrikethroughStyle(rawValue: validate(foundationValue))!)
case .textColor: ret = .textColor(validate(foundationValue))
case .foregroundColor: ret = .textColor(validate(foundationValue))
case .textEffect: ret = .textEffect(TextEffect(rawValue: validate(foundationValue))!)
case .underlineColor: ret = .underlineColor(validate(foundationValue))
case .underlineStyle: ret = .underlineStyle(UnderlineStyle(rawValue: validate(foundationValue))!)
Expand All @@ -195,9 +201,9 @@ public enum Attribute {
}

/// The key name corresponding to the attribute.
public var keyName: String {
public var keyName: StringKey {

var name: Attribute.Name!
var name: AttributeName!

// Bug in Swift prevents us from putting directives inside switch statements (https://bugs.swift.org/browse/SR-2)

Expand Down Expand Up @@ -236,7 +242,7 @@ public enum Attribute {
case .strokeWidth(_): name = .strokeWidth
case .strikethroughColor(_): name = .strikethroughColor
case .strikethroughStyle(_): name = .strikethroughStyle
case .textColor(_): name = .textColor
case .textColor(_): name = .foregroundColor
case .textEffect(_): name = .textEffect
case .underlineColor(_): name = .underlineColor
case .underlineStyle(_): name = .underlineStyle
Expand All @@ -245,7 +251,11 @@ public enum Attribute {
default: break
}

return name.rawValue
#if swift(>=4.0)
return name
#else
return name.rawValue
#endif
}

// Convenience getter variable for the associated value of the attribute. See each case to determine the return type.
Expand Down
Loading

0 comments on commit 33939dd

Please sign in to comment.