Skip to content

Commit

Permalink
Fix synchronization
Browse files Browse the repository at this point in the history
  • Loading branch information
koher committed Jan 14, 2017
1 parent 9a57b25 commit 7e5ff83
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 49 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import PackageDescription
let package = Package(
name: "TweetupKit",
dependencies: [
.Package(url: "https://github.com/swift-tweets/OAuthSwift.git", "1.2.0-beta")
.Package(url: "https://github.com/swift-tweets/OAuthSwift.git", "2.0.0-beta")
]
)
88 changes: 60 additions & 28 deletions Sources/CodeRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,65 @@ import Foundation
internal class CodeRenderer: NSObject {
private var webView: WebView!
fileprivate var loading = true
fileprivate var _image: CGImage!
fileprivate var error: Error?
private var getImage: (() throws -> CGImage)? = nil
private var completions: [(() throws -> CGImage) -> ()] = []
private var zelf: CodeRenderer? // not to released during the async operation

fileprivate static let height: CGFloat = 736

init(url: String) {
super.init()
zelf = self

webView = WebView(frame: NSRect(x: 0, y: 0, width: 414, height: CodeRenderer.height))
webView.frameLoadDelegate = self
webView.customUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"
webView.mainFrameURL = url

let runLoop = RunLoop.current
while loading && runLoop.run(mode: .defaultRunLoopMode, before: .distantFuture) { }
DispatchQueue.main.async {
self.webView = WebView(frame: NSRect(x: 0, y: 0, width: 414, height: CodeRenderer.height))
self.webView.frameLoadDelegate = self
self.webView.customUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"
self.webView.mainFrameURL = url
}
}

func image() throws -> CGImage {
if let error = self.error {
throw error
func image(completion: @escaping (() throws -> CGImage) -> ()) {
DispatchQueue.main.async {
if let getImage = self.getImage {
completion {
try getImage()
}
}

self.completions.append(completion)
}

return _image
}

func writeImage(to path: String) throws {
let image = try self.image()
let url = URL(fileURLWithPath: path)
guard let destination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypePNG, 1, nil) else {
throw CodeRendererError.writingFailed
func writeImage(to path: String, completion: @escaping (() throws -> ()) -> ()) {
image { getImage in
completion {
let image = try getImage()
let url = URL(fileURLWithPath: path)
guard let destination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypePNG, 1, nil) else {
throw CodeRendererError.writingFailed
}

CGImageDestinationAddImage(destination, image, nil)

guard CGImageDestinationFinalize(destination) else {
throw CodeRendererError.writingFailed
}
}
}
CGImageDestinationAddImage(destination, image, nil)
guard CGImageDestinationFinalize(destination) else {
throw CodeRendererError.writingFailed
}

fileprivate func resolve(getImage: @escaping (() throws -> CGImage)) {
for completion in completions {
completion(getImage)
}
completions.removeAll()
self.getImage = getImage
self.zelf = nil
}
}

extension CodeRenderer: WebFrameLoadDelegate {
extension CodeRenderer: WebFrameLoadDelegate { // called on the main thread
func webView(_ sender: WebView, didFinishLoadFor frame: WebFrame) {
let document = frame.domDocument!
let body = document.getElementsByTagName("body").item(0)!
Expand All @@ -53,7 +71,7 @@ extension CodeRenderer: WebFrameLoadDelegate {

let files = document.getElementsByClassName("blob-file-content")!
guard files.length > 0 else {
error = CodeRendererError.illegalResponse
resolve(getImage: { throw CodeRendererError.illegalResponse } )
return
}
let code = files.item(0) as! DOMElement
Expand All @@ -78,13 +96,27 @@ extension CodeRenderer: WebFrameLoadDelegate {
context.draw(imageRep.cgImage!, in: targetRect)

let provider: CGDataProvider = CGDataProvider(data: Data(bytes: pixels) as CFData)!
_image = CGImage(width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: width * 4, space: colorSpace, bitmapInfo: bitmapInfo, provider: provider, decode: nil, shouldInterpolate: false, intent: .defaultIntent)
resolve(getImage: {
CGImage(
width: width,
height: height,
bitsPerComponent: 8,
bitsPerPixel: 32,
bytesPerRow: width * 4,
space: colorSpace,
bitmapInfo: bitmapInfo,
provider: provider,
decode: nil,
shouldInterpolate: false,
intent: .defaultIntent
)!
})

loading = false
}

func webView(_ sender: WebView, didFailLoadWithError error: Error, for frame: WebFrame) {
self.error = error
resolve(getImage: { throw error })
loading = false
}
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Gist.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Foundation

internal struct Gist {
static func createGist(description: String, code: Code, accessToken: String, callback: @escaping (() throws -> String) -> ()) {
let session = URLSession(configuration: .ephemeral)
let session = URLSession(configuration: .ephemeral, delegate: nil, delegateQueue: .current)
var request = URLRequest(url: URL(string: "https://api.github.com/gists")!)
request.httpMethod = "POST"
request.addValue("token \(accessToken)", forHTTPHeaderField: "Authorization")
Expand Down
22 changes: 7 additions & 15 deletions Sources/Speaker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public struct Speaker {
return
}

// TODO
let resolve = flatten(flatten(resolveCode, resolveGist), resolveImage)
resolve(tweet) { getTweet in
do {
Expand Down Expand Up @@ -167,20 +166,13 @@ public struct Speaker {
return
}

DispatchQueue.main.async {
do {
let url = "https://gist.github.com/\(id)"
let imagePath = outputDirectoryPath.appendingPathComponent("\(id).png")
let codeRenderer = CodeRenderer(url: url)
try codeRenderer.writeImage(to: Speaker.imagePath(imagePath, from: self.baseDirectoryPath))

callback {
return try Tweet(body: "\(tweet.body)", attachment: .image(Image(alternativeText: image.alternativeText, source: .local(imagePath))))
}
} catch let error {
callback {
throw error
}
let url = "https://gist.github.com/\(id)"
let imagePath = outputDirectoryPath.appendingPathComponent("\(id).png")
let codeRenderer = CodeRenderer(url: url)
codeRenderer.writeImage(to: Speaker.imagePath(imagePath, from: self.baseDirectoryPath)) { getVoid in
callback {
try getVoid()
return try Tweet(body: "\(tweet.body)", attachment: .image(Image(alternativeText: image.alternativeText, source: .local(imagePath))))
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion Sources/Twitter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import Foundation
internal struct Twitter {
static func update(status: String, mediaId: String? = nil, credential: OAuthCredential, callback: @escaping (() throws -> (String, String)) -> ()) {
let client = OAuthSwiftClient(credential: credential)

client.sessionFactory.queue = { .current }

var parameters = [
"status": status
]
Expand All @@ -24,6 +25,7 @@ internal struct Twitter {

static func upload(media: Data, credential: OAuthCredential, callback: @escaping (() throws -> String) -> ()) {
let client = OAuthSwiftClient(credential: credential)
client.sessionFactory.queue = { .current }

_ = client.post(
"https://upload.twitter.com/1.1/media/upload.json",
Expand Down
6 changes: 3 additions & 3 deletions Tests/TweetupKitTests/SpeakerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class SpeakerTests: XCTestCase {

let string = "Twinkle, twinkle, little star,\nHow I wonder what you are! \(start)\n\n---\n\nUp above the world so high,\nLike a diamond in the sky. \(start)\n\n```swift:hello.swift\nlet name = \"Swift\"\nprint(\"Hello \\(name)!\")\n```\n\n---\n\nTwinkle, twinkle, little star,\nHow I wonder what you are! \(start)\n\n![](\(imagePath))" // includes `start` to avoid duplicate tweets
let tweets = try! Tweet.tweets(from: string)
speaker.post(tweets: tweets, with: 30.0) { getIds in
speaker.post(tweets: tweets, with: 5.0) { getIds in
defer {
expectation.fulfill()
}
Expand All @@ -50,11 +50,11 @@ class SpeakerTests: XCTestCase {
}
}

waitForExpectations(timeout: 89.0, handler: nil)
waitForExpectations(timeout: 14.0, handler: nil)

let end = Date.timeIntervalSinceReferenceDate

XCTAssertGreaterThan(end - start, 60.0)
XCTAssertGreaterThan(end - start, 10.0)
}

do { // error duraing posting tweets
Expand Down

0 comments on commit 7e5ff83

Please sign in to comment.